mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-07-16 13:19:56 -04:00
Merge git://git.kernel.org/pub/scm/linux/kernel/git/davem/net-next
Pull networking updates from David Miller:
1) Add TX fast path in mac80211, from Johannes Berg.
2) Add TSO/GRO support to ibmveth, from Thomas Falcon
3) Move away from cached routes in ipv6, just like ipv4, from Martin
KaFai Lau.
4) Lots of new rhashtable tests, from Thomas Graf.
5) Run ingress qdisc lockless, from Alexei Starovoitov.
6) Allow servers to fetch TCP packet headers for SYN packets of new
connections, for fingerprinting. From Eric Dumazet.
7) Add mode parameter to pktgen, for testing receive. From Alexei
Starovoitov.
8) Cache access optimizations via simplifications of build_skb(), from
Alexander Duyck.
9) Move page frag allocator under mm/, also from Alexander.
10) Add xmit_more support to hv_netvsc, from KY Srinivasan.
11) Add a counter guard in case we try to perform endless reclassify
loops in the packet scheduler.
12) Extern flow dissector to be programmable and use it in new "Flower"
classifier. From Jiri Pirko.
13) AF_PACKET fanout rollover fixes, performance improvements, and new
statistics. From Willem de Bruijn.
14) Add netdev driver for GENEVE tunnels, from John W Linville.
15) Add ingress netfilter hooks and filtering, from Pablo Neira Ayuso.
16) Fix handling of epoll edge triggers in TCP, from Eric Dumazet.
17) Add an ECN retry fallback for the initial TCP handshake, from Daniel
Borkmann.
18) Add tail call support to BPF, from Alexei Starovoitov.
19) Add several pktgen helper scripts, from Jesper Dangaard Brouer.
20) Add zerocopy support to AF_UNIX, from Hannes Frederic Sowa.
21) Favor even port numbers for allocation to connect() requests, and
odd port numbers for bind(0), in an effort to help avoid
ip_local_port_range exhaustion. From Eric Dumazet.
22) Add Cavium ThunderX driver, from Sunil Goutham.
23) Allow bpf programs to access skb_iif and dev->ifindex SKB metadata,
from Alexei Starovoitov.
24) Add support for T6 chips in cxgb4vf driver, from Hariprasad Shenai.
25) Double TCP Small Queues default to 256K to accomodate situations
like the XEN driver and wireless aggregation. From Wei Liu.
26) Add more entropy inputs to flow dissector, from Tom Herbert.
27) Add CDG congestion control algorithm to TCP, from Kenneth Klette
Jonassen.
28) Convert ipset over to RCU locking, from Jozsef Kadlecsik.
29) Track and act upon link status of ipv4 route nexthops, from Andy
Gospodarek.
* git://git.kernel.org/pub/scm/linux/kernel/git/davem/net-next: (1670 commits)
bridge: vlan: flush the dynamically learned entries on port vlan delete
bridge: multicast: add a comment to br_port_state_selection about blocking state
net: inet_diag: export IPV6_V6ONLY sockopt
stmmac: troubleshoot unexpected bits in des0 & des1
net: ipv4 sysctl option to ignore routes when nexthop link is down
net: track link-status of ipv4 nexthops
net: switchdev: ignore unsupported bridge flags
net: Cavium: Fix MAC address setting in shutdown state
drivers: net: xgene: fix for ACPI support without ACPI
ip: report the original address of ICMP messages
net/mlx5e: Prefetch skb data on RX
net/mlx5e: Pop cq outside mlx5e_get_cqe
net/mlx5e: Remove mlx5e_cq.sqrq back-pointer
net/mlx5e: Remove extra spaces
net/mlx5e: Avoid TX CQE generation if more xmit packets expected
net/mlx5e: Avoid redundant dev_kfree_skb() upon NOP completion
net/mlx5e: Remove re-assignment of wq type in mlx5e_enable_rq()
net/mlx5e: Use skb_shinfo(skb)->gso_segs rather than counting them
net/mlx5e: Static mapping of netdev priv resources to/from netdev TX queues
net/mlx4_en: Use HW counters for rx/tx bytes/packets in PF device
...
This commit is contained in:
@@ -618,6 +618,92 @@ static int vlan_ioctl_handler(struct net *net, void __user *arg)
|
||||
return err;
|
||||
}
|
||||
|
||||
static struct sk_buff **vlan_gro_receive(struct sk_buff **head,
|
||||
struct sk_buff *skb)
|
||||
{
|
||||
struct sk_buff *p, **pp = NULL;
|
||||
struct vlan_hdr *vhdr;
|
||||
unsigned int hlen, off_vlan;
|
||||
const struct packet_offload *ptype;
|
||||
__be16 type;
|
||||
int flush = 1;
|
||||
|
||||
off_vlan = skb_gro_offset(skb);
|
||||
hlen = off_vlan + sizeof(*vhdr);
|
||||
vhdr = skb_gro_header_fast(skb, off_vlan);
|
||||
if (skb_gro_header_hard(skb, hlen)) {
|
||||
vhdr = skb_gro_header_slow(skb, hlen, off_vlan);
|
||||
if (unlikely(!vhdr))
|
||||
goto out;
|
||||
}
|
||||
|
||||
type = vhdr->h_vlan_encapsulated_proto;
|
||||
|
||||
rcu_read_lock();
|
||||
ptype = gro_find_receive_by_type(type);
|
||||
if (!ptype)
|
||||
goto out_unlock;
|
||||
|
||||
flush = 0;
|
||||
|
||||
for (p = *head; p; p = p->next) {
|
||||
struct vlan_hdr *vhdr2;
|
||||
|
||||
if (!NAPI_GRO_CB(p)->same_flow)
|
||||
continue;
|
||||
|
||||
vhdr2 = (struct vlan_hdr *)(p->data + off_vlan);
|
||||
if (compare_vlan_header(vhdr, vhdr2))
|
||||
NAPI_GRO_CB(p)->same_flow = 0;
|
||||
}
|
||||
|
||||
skb_gro_pull(skb, sizeof(*vhdr));
|
||||
skb_gro_postpull_rcsum(skb, vhdr, sizeof(*vhdr));
|
||||
pp = ptype->callbacks.gro_receive(head, skb);
|
||||
|
||||
out_unlock:
|
||||
rcu_read_unlock();
|
||||
out:
|
||||
NAPI_GRO_CB(skb)->flush |= flush;
|
||||
|
||||
return pp;
|
||||
}
|
||||
|
||||
static int vlan_gro_complete(struct sk_buff *skb, int nhoff)
|
||||
{
|
||||
struct vlan_hdr *vhdr = (struct vlan_hdr *)(skb->data + nhoff);
|
||||
__be16 type = vhdr->h_vlan_encapsulated_proto;
|
||||
struct packet_offload *ptype;
|
||||
int err = -ENOENT;
|
||||
|
||||
rcu_read_lock();
|
||||
ptype = gro_find_complete_by_type(type);
|
||||
if (ptype)
|
||||
err = ptype->callbacks.gro_complete(skb, nhoff + sizeof(*vhdr));
|
||||
|
||||
rcu_read_unlock();
|
||||
return err;
|
||||
}
|
||||
|
||||
static struct packet_offload vlan_packet_offloads[] __read_mostly = {
|
||||
{
|
||||
.type = cpu_to_be16(ETH_P_8021Q),
|
||||
.priority = 10,
|
||||
.callbacks = {
|
||||
.gro_receive = vlan_gro_receive,
|
||||
.gro_complete = vlan_gro_complete,
|
||||
},
|
||||
},
|
||||
{
|
||||
.type = cpu_to_be16(ETH_P_8021AD),
|
||||
.priority = 10,
|
||||
.callbacks = {
|
||||
.gro_receive = vlan_gro_receive,
|
||||
.gro_complete = vlan_gro_complete,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
static int __net_init vlan_init_net(struct net *net)
|
||||
{
|
||||
struct vlan_net *vn = net_generic(net, vlan_net_id);
|
||||
@@ -645,6 +731,7 @@ static struct pernet_operations vlan_net_ops = {
|
||||
static int __init vlan_proto_init(void)
|
||||
{
|
||||
int err;
|
||||
unsigned int i;
|
||||
|
||||
pr_info("%s v%s\n", vlan_fullname, vlan_version);
|
||||
|
||||
@@ -668,6 +755,9 @@ static int __init vlan_proto_init(void)
|
||||
if (err < 0)
|
||||
goto err5;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(vlan_packet_offloads); i++)
|
||||
dev_add_offload(&vlan_packet_offloads[i]);
|
||||
|
||||
vlan_ioctl_set(vlan_ioctl_handler);
|
||||
return 0;
|
||||
|
||||
@@ -685,7 +775,13 @@ static int __init vlan_proto_init(void)
|
||||
|
||||
static void __exit vlan_cleanup_module(void)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
vlan_ioctl_set(NULL);
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(vlan_packet_offloads); i++)
|
||||
dev_remove_offload(&vlan_packet_offloads[i]);
|
||||
|
||||
vlan_netlink_fini();
|
||||
|
||||
unregister_netdevice_notifier(&vlan_notifier_block);
|
||||
|
||||
@@ -45,6 +45,9 @@ config COMPAT_NETLINK_MESSAGES
|
||||
Newly written code should NEVER need this option but do
|
||||
compat-independent messages instead!
|
||||
|
||||
config NET_INGRESS
|
||||
bool
|
||||
|
||||
menu "Networking options"
|
||||
|
||||
source "net/packet/Kconfig"
|
||||
|
||||
@@ -1030,7 +1030,7 @@ static int atalk_create(struct net *net, struct socket *sock, int protocol,
|
||||
if (sock->type != SOCK_RAW && sock->type != SOCK_DGRAM)
|
||||
goto out;
|
||||
rc = -ENOMEM;
|
||||
sk = sk_alloc(net, PF_APPLETALK, GFP_KERNEL, &ddp_proto);
|
||||
sk = sk_alloc(net, PF_APPLETALK, GFP_KERNEL, &ddp_proto, kern);
|
||||
if (!sk)
|
||||
goto out;
|
||||
rc = 0;
|
||||
|
||||
@@ -141,7 +141,7 @@ static struct proto vcc_proto = {
|
||||
.release_cb = vcc_release_cb,
|
||||
};
|
||||
|
||||
int vcc_create(struct net *net, struct socket *sock, int protocol, int family)
|
||||
int vcc_create(struct net *net, struct socket *sock, int protocol, int family, int kern)
|
||||
{
|
||||
struct sock *sk;
|
||||
struct atm_vcc *vcc;
|
||||
@@ -149,7 +149,7 @@ int vcc_create(struct net *net, struct socket *sock, int protocol, int family)
|
||||
sock->sk = NULL;
|
||||
if (sock->type == SOCK_STREAM)
|
||||
return -EINVAL;
|
||||
sk = sk_alloc(net, family, GFP_KERNEL, &vcc_proto);
|
||||
sk = sk_alloc(net, family, GFP_KERNEL, &vcc_proto, kern);
|
||||
if (!sk)
|
||||
return -ENOMEM;
|
||||
sock_init_data(sock, sk);
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
#include <linux/poll.h> /* for poll_table */
|
||||
|
||||
|
||||
int vcc_create(struct net *net, struct socket *sock, int protocol, int family);
|
||||
int vcc_create(struct net *net, struct socket *sock, int protocol, int family, int kern);
|
||||
int vcc_release(struct socket *sock);
|
||||
int vcc_connect(struct socket *sock, int itf, short vpi, int vci);
|
||||
int vcc_recvmsg(struct socket *sock, struct msghdr *msg, size_t size,
|
||||
|
||||
@@ -136,7 +136,7 @@ static int pvc_create(struct net *net, struct socket *sock, int protocol,
|
||||
return -EAFNOSUPPORT;
|
||||
|
||||
sock->ops = &pvc_proto_ops;
|
||||
return vcc_create(net, sock, protocol, PF_ATMPVC);
|
||||
return vcc_create(net, sock, protocol, PF_ATMPVC, kern);
|
||||
}
|
||||
|
||||
static const struct net_proto_family pvc_family_ops = {
|
||||
|
||||
@@ -660,7 +660,7 @@ static int svc_create(struct net *net, struct socket *sock, int protocol,
|
||||
return -EAFNOSUPPORT;
|
||||
|
||||
sock->ops = &svc_proto_ops;
|
||||
error = vcc_create(net, sock, protocol, AF_ATMSVC);
|
||||
error = vcc_create(net, sock, protocol, AF_ATMSVC, kern);
|
||||
if (error)
|
||||
return error;
|
||||
ATM_SD(sock)->local.sas_family = AF_ATMSVC;
|
||||
|
||||
@@ -40,7 +40,6 @@
|
||||
#include <linux/notifier.h>
|
||||
#include <linux/proc_fs.h>
|
||||
#include <linux/stat.h>
|
||||
#include <linux/netfilter.h>
|
||||
#include <linux/sysctl.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/spinlock.h>
|
||||
@@ -855,7 +854,7 @@ static int ax25_create(struct net *net, struct socket *sock, int protocol,
|
||||
return -ESOCKTNOSUPPORT;
|
||||
}
|
||||
|
||||
sk = sk_alloc(net, PF_AX25, GFP_ATOMIC, &ax25_proto);
|
||||
sk = sk_alloc(net, PF_AX25, GFP_ATOMIC, &ax25_proto, kern);
|
||||
if (sk == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
@@ -881,7 +880,7 @@ struct sock *ax25_make_new(struct sock *osk, struct ax25_dev *ax25_dev)
|
||||
struct sock *sk;
|
||||
ax25_cb *ax25, *oax25;
|
||||
|
||||
sk = sk_alloc(sock_net(osk), PF_AX25, GFP_ATOMIC, osk->sk_prot);
|
||||
sk = sk_alloc(sock_net(osk), PF_AX25, GFP_ATOMIC, osk->sk_prot, 0);
|
||||
if (sk == NULL)
|
||||
return NULL;
|
||||
|
||||
|
||||
@@ -23,7 +23,6 @@
|
||||
#include <linux/inet.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <linux/netfilter.h>
|
||||
#include <net/sock.h>
|
||||
#include <net/tcp_states.h>
|
||||
#include <asm/uaccess.h>
|
||||
|
||||
@@ -31,7 +31,6 @@
|
||||
#include <linux/notifier.h>
|
||||
#include <linux/proc_fs.h>
|
||||
#include <linux/stat.h>
|
||||
#include <linux/netfilter.h>
|
||||
#include <linux/sysctl.h>
|
||||
#include <net/ip.h>
|
||||
#include <net/arp.h>
|
||||
|
||||
@@ -24,7 +24,6 @@
|
||||
#include <linux/inet.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <linux/netfilter.h>
|
||||
#include <net/sock.h>
|
||||
#include <asm/uaccess.h>
|
||||
#include <linux/fcntl.h>
|
||||
|
||||
@@ -34,7 +34,6 @@
|
||||
#include <linux/proc_fs.h>
|
||||
#include <linux/seq_file.h>
|
||||
#include <linux/stat.h>
|
||||
#include <linux/netfilter.h>
|
||||
#include <linux/sysctl.h>
|
||||
#include <linux/export.h>
|
||||
#include <net/ip.h>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#
|
||||
# Copyright (C) 2007-2014 B.A.T.M.A.N. contributors:
|
||||
# Copyright (C) 2007-2015 B.A.T.M.A.N. contributors:
|
||||
#
|
||||
# Marek Lindner, Simon Wunderlich
|
||||
#
|
||||
@@ -20,7 +20,7 @@ obj-$(CONFIG_BATMAN_ADV) += batman-adv.o
|
||||
batman-adv-y += bat_iv_ogm.o
|
||||
batman-adv-y += bitarray.o
|
||||
batman-adv-$(CONFIG_BATMAN_ADV_BLA) += bridge_loop_avoidance.o
|
||||
batman-adv-y += debugfs.o
|
||||
batman-adv-$(CONFIG_DEBUG_FS) += debugfs.o
|
||||
batman-adv-$(CONFIG_BATMAN_ADV_DAT) += distributed-arp-table.o
|
||||
batman-adv-y += fragmentation.o
|
||||
batman-adv-y += gateway_client.o
|
||||
@@ -29,6 +29,7 @@ batman-adv-y += hard-interface.o
|
||||
batman-adv-y += hash.o
|
||||
batman-adv-y += icmp_socket.o
|
||||
batman-adv-y += main.o
|
||||
batman-adv-$(CONFIG_BATMAN_ADV_MCAST) += multicast.o
|
||||
batman-adv-$(CONFIG_BATMAN_ADV_NC) += network-coding.o
|
||||
batman-adv-y += originator.o
|
||||
batman-adv-y += routing.o
|
||||
@@ -36,4 +37,3 @@ batman-adv-y += send.o
|
||||
batman-adv-y += soft-interface.o
|
||||
batman-adv-y += sysfs.o
|
||||
batman-adv-y += translation-table.o
|
||||
batman-adv-$(CONFIG_BATMAN_ADV_MCAST) += multicast.o
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2011-2014 B.A.T.M.A.N. contributors:
|
||||
/* Copyright (C) 2011-2015 B.A.T.M.A.N. contributors:
|
||||
*
|
||||
* Marek Lindner
|
||||
*
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2007-2014 B.A.T.M.A.N. contributors:
|
||||
/* Copyright (C) 2007-2015 B.A.T.M.A.N. contributors:
|
||||
*
|
||||
* Marek Lindner, Simon Wunderlich
|
||||
*
|
||||
@@ -15,20 +15,50 @@
|
||||
* along with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "main.h"
|
||||
#include "translation-table.h"
|
||||
#include "originator.h"
|
||||
#include "routing.h"
|
||||
#include "gateway_common.h"
|
||||
#include "gateway_client.h"
|
||||
#include "hard-interface.h"
|
||||
#include "send.h"
|
||||
#include "bat_algo.h"
|
||||
#include "main.h"
|
||||
|
||||
#include <linux/atomic.h>
|
||||
#include <linux/bitmap.h>
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/bug.h>
|
||||
#include <linux/byteorder/generic.h>
|
||||
#include <linux/cache.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/etherdevice.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/if_ether.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/jiffies.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/pkt_sched.h>
|
||||
#include <linux/printk.h>
|
||||
#include <linux/random.h>
|
||||
#include <linux/rculist.h>
|
||||
#include <linux/rcupdate.h>
|
||||
#include <linux/seq_file.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/stddef.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/workqueue.h>
|
||||
|
||||
#include "bitarray.h"
|
||||
#include "hard-interface.h"
|
||||
#include "hash.h"
|
||||
#include "network-coding.h"
|
||||
#include "originator.h"
|
||||
#include "packet.h"
|
||||
#include "routing.h"
|
||||
#include "send.h"
|
||||
#include "translation-table.h"
|
||||
|
||||
/**
|
||||
* enum batadv_dup_status - duplicate status
|
||||
* @BATADV_NO_DUP: the packet is a duplicate
|
||||
* @BATADV_NO_DUP: the packet is no duplicate
|
||||
* @BATADV_ORIG_DUP: OGM is a duplicate in the originator (but not for the
|
||||
* neighbor)
|
||||
* @BATADV_NEIGH_DUP: OGM is a duplicate for the neighbor
|
||||
@@ -55,7 +85,7 @@ static void batadv_ring_buffer_set(uint8_t lq_recv[], uint8_t *lq_index,
|
||||
}
|
||||
|
||||
/**
|
||||
* batadv_ring_buffer_set - compute the average of all non-zero values stored
|
||||
* batadv_ring_buffer_avg - compute the average of all non-zero values stored
|
||||
* in the given ring buffer
|
||||
* @lq_recv: pointer to the ring buffer
|
||||
*
|
||||
@@ -64,7 +94,9 @@ static void batadv_ring_buffer_set(uint8_t lq_recv[], uint8_t *lq_index,
|
||||
static uint8_t batadv_ring_buffer_avg(const uint8_t lq_recv[])
|
||||
{
|
||||
const uint8_t *ptr;
|
||||
uint16_t count = 0, i = 0, sum = 0;
|
||||
uint16_t count = 0;
|
||||
uint16_t i = 0;
|
||||
uint16_t sum = 0;
|
||||
|
||||
ptr = lq_recv;
|
||||
|
||||
@@ -308,7 +340,6 @@ static int batadv_iv_ogm_iface_enable(struct batadv_hard_iface *hard_iface)
|
||||
struct batadv_ogm_packet *batadv_ogm_packet;
|
||||
unsigned char *ogm_buff;
|
||||
uint32_t random_seqno;
|
||||
int res = -ENOMEM;
|
||||
|
||||
/* randomize initial seqno to avoid collision */
|
||||
get_random_bytes(&random_seqno, sizeof(random_seqno));
|
||||
@@ -317,7 +348,7 @@ static int batadv_iv_ogm_iface_enable(struct batadv_hard_iface *hard_iface)
|
||||
hard_iface->bat_iv.ogm_buff_len = BATADV_OGM_HLEN;
|
||||
ogm_buff = kmalloc(hard_iface->bat_iv.ogm_buff_len, GFP_ATOMIC);
|
||||
if (!ogm_buff)
|
||||
goto out;
|
||||
return -ENOMEM;
|
||||
|
||||
hard_iface->bat_iv.ogm_buff = ogm_buff;
|
||||
|
||||
@@ -329,10 +360,7 @@ static int batadv_iv_ogm_iface_enable(struct batadv_hard_iface *hard_iface)
|
||||
batadv_ogm_packet->reserved = 0;
|
||||
batadv_ogm_packet->tq = BATADV_TQ_MAX_VALUE;
|
||||
|
||||
res = 0;
|
||||
|
||||
out:
|
||||
return res;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void batadv_iv_ogm_iface_disable(struct batadv_hard_iface *hard_iface)
|
||||
@@ -396,8 +424,8 @@ static uint8_t batadv_hop_penalty(uint8_t tq,
|
||||
}
|
||||
|
||||
/* is there another aggregated packet here? */
|
||||
static int batadv_iv_ogm_aggr_packet(int buff_pos, int packet_len,
|
||||
__be16 tvlv_len)
|
||||
static bool batadv_iv_ogm_aggr_packet(int buff_pos, int packet_len,
|
||||
__be16 tvlv_len)
|
||||
{
|
||||
int next_buff_pos = 0;
|
||||
|
||||
@@ -413,7 +441,7 @@ static void batadv_iv_ogm_send_to_if(struct batadv_forw_packet *forw_packet,
|
||||
struct batadv_hard_iface *hard_iface)
|
||||
{
|
||||
struct batadv_priv *bat_priv = netdev_priv(hard_iface->soft_iface);
|
||||
char *fwd_str;
|
||||
const char *fwd_str;
|
||||
uint8_t packet_num;
|
||||
int16_t buff_pos;
|
||||
struct batadv_ogm_packet *batadv_ogm_packet;
|
||||
@@ -451,7 +479,7 @@ static void batadv_iv_ogm_send_to_if(struct batadv_forw_packet *forw_packet,
|
||||
batadv_ogm_packet->orig,
|
||||
ntohl(batadv_ogm_packet->seqno),
|
||||
batadv_ogm_packet->tq, batadv_ogm_packet->ttl,
|
||||
(batadv_ogm_packet->flags & BATADV_DIRECTLINK ?
|
||||
((batadv_ogm_packet->flags & BATADV_DIRECTLINK) ?
|
||||
"on" : "off"),
|
||||
hard_iface->net_dev->name,
|
||||
hard_iface->net_dev->dev_addr);
|
||||
@@ -548,58 +576,62 @@ batadv_iv_ogm_can_aggregate(const struct batadv_ogm_packet *new_bat_ogm_packet,
|
||||
* - the send time is within our MAX_AGGREGATION_MS time
|
||||
* - the resulting packet wont be bigger than
|
||||
* MAX_AGGREGATION_BYTES
|
||||
* otherwise aggregation is not possible
|
||||
*/
|
||||
if (time_before(send_time, forw_packet->send_time) &&
|
||||
time_after_eq(aggregation_end_time, forw_packet->send_time) &&
|
||||
(aggregated_bytes <= BATADV_MAX_AGGREGATION_BYTES)) {
|
||||
/* check aggregation compatibility
|
||||
* -> direct link packets are broadcasted on
|
||||
* their interface only
|
||||
* -> aggregate packet if the current packet is
|
||||
* a "global" packet as well as the base
|
||||
* packet
|
||||
*/
|
||||
primary_if = batadv_primary_if_get_selected(bat_priv);
|
||||
if (!primary_if)
|
||||
goto out;
|
||||
if (!time_before(send_time, forw_packet->send_time) ||
|
||||
!time_after_eq(aggregation_end_time, forw_packet->send_time))
|
||||
return false;
|
||||
|
||||
/* packet is not leaving on the same interface. */
|
||||
if (forw_packet->if_outgoing != if_outgoing)
|
||||
goto out;
|
||||
if (aggregated_bytes > BATADV_MAX_AGGREGATION_BYTES)
|
||||
return false;
|
||||
|
||||
/* packets without direct link flag and high TTL
|
||||
* are flooded through the net
|
||||
*/
|
||||
if ((!directlink) &&
|
||||
(!(batadv_ogm_packet->flags & BATADV_DIRECTLINK)) &&
|
||||
(batadv_ogm_packet->ttl != 1) &&
|
||||
/* packet is not leaving on the same interface. */
|
||||
if (forw_packet->if_outgoing != if_outgoing)
|
||||
return false;
|
||||
|
||||
/* own packets originating non-primary
|
||||
* interfaces leave only that interface
|
||||
*/
|
||||
((!forw_packet->own) ||
|
||||
(forw_packet->if_incoming == primary_if))) {
|
||||
res = true;
|
||||
goto out;
|
||||
}
|
||||
/* check aggregation compatibility
|
||||
* -> direct link packets are broadcasted on
|
||||
* their interface only
|
||||
* -> aggregate packet if the current packet is
|
||||
* a "global" packet as well as the base
|
||||
* packet
|
||||
*/
|
||||
primary_if = batadv_primary_if_get_selected(bat_priv);
|
||||
if (!primary_if)
|
||||
return false;
|
||||
|
||||
/* if the incoming packet is sent via this one
|
||||
* interface only - we still can aggregate
|
||||
*/
|
||||
if ((directlink) &&
|
||||
(new_bat_ogm_packet->ttl == 1) &&
|
||||
(forw_packet->if_incoming == if_incoming) &&
|
||||
/* packets without direct link flag and high TTL
|
||||
* are flooded through the net
|
||||
*/
|
||||
if (!directlink &&
|
||||
!(batadv_ogm_packet->flags & BATADV_DIRECTLINK) &&
|
||||
batadv_ogm_packet->ttl != 1 &&
|
||||
|
||||
/* packets from direct neighbors or
|
||||
* own secondary interface packets
|
||||
* (= secondary interface packets in general)
|
||||
*/
|
||||
(batadv_ogm_packet->flags & BATADV_DIRECTLINK ||
|
||||
(forw_packet->own &&
|
||||
forw_packet->if_incoming != primary_if))) {
|
||||
res = true;
|
||||
goto out;
|
||||
}
|
||||
/* own packets originating non-primary
|
||||
* interfaces leave only that interface
|
||||
*/
|
||||
(!forw_packet->own ||
|
||||
forw_packet->if_incoming == primary_if)) {
|
||||
res = true;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* if the incoming packet is sent via this one
|
||||
* interface only - we still can aggregate
|
||||
*/
|
||||
if (directlink &&
|
||||
new_bat_ogm_packet->ttl == 1 &&
|
||||
forw_packet->if_incoming == if_incoming &&
|
||||
|
||||
/* packets from direct neighbors or
|
||||
* own secondary interface packets
|
||||
* (= secondary interface packets in general)
|
||||
*/
|
||||
(batadv_ogm_packet->flags & BATADV_DIRECTLINK ||
|
||||
(forw_packet->own &&
|
||||
forw_packet->if_incoming != primary_if))) {
|
||||
res = true;
|
||||
goto out;
|
||||
}
|
||||
|
||||
out:
|
||||
@@ -642,19 +674,16 @@ static void batadv_iv_ogm_aggregate_new(const unsigned char *packet_buff,
|
||||
if (!batadv_atomic_dec_not_zero(&bat_priv->batman_queue_left)) {
|
||||
batadv_dbg(BATADV_DBG_BATMAN, bat_priv,
|
||||
"batman packet queue full\n");
|
||||
goto out;
|
||||
goto out_free_outgoing;
|
||||
}
|
||||
}
|
||||
|
||||
forw_packet_aggr = kmalloc(sizeof(*forw_packet_aggr), GFP_ATOMIC);
|
||||
if (!forw_packet_aggr) {
|
||||
if (!own_packet)
|
||||
atomic_inc(&bat_priv->batman_queue_left);
|
||||
goto out;
|
||||
}
|
||||
if (!forw_packet_aggr)
|
||||
goto out_nomem;
|
||||
|
||||
if ((atomic_read(&bat_priv->aggregated_ogms)) &&
|
||||
(packet_len < BATADV_MAX_AGGREGATION_BYTES))
|
||||
if (atomic_read(&bat_priv->aggregated_ogms) &&
|
||||
packet_len < BATADV_MAX_AGGREGATION_BYTES)
|
||||
skb_size = BATADV_MAX_AGGREGATION_BYTES;
|
||||
else
|
||||
skb_size = packet_len;
|
||||
@@ -662,12 +691,8 @@ static void batadv_iv_ogm_aggregate_new(const unsigned char *packet_buff,
|
||||
skb_size += ETH_HLEN;
|
||||
|
||||
forw_packet_aggr->skb = netdev_alloc_skb_ip_align(NULL, skb_size);
|
||||
if (!forw_packet_aggr->skb) {
|
||||
if (!own_packet)
|
||||
atomic_inc(&bat_priv->batman_queue_left);
|
||||
kfree(forw_packet_aggr);
|
||||
goto out;
|
||||
}
|
||||
if (!forw_packet_aggr->skb)
|
||||
goto out_free_forw_packet;
|
||||
forw_packet_aggr->skb->priority = TC_PRIO_CONTROL;
|
||||
skb_reserve(forw_packet_aggr->skb, ETH_HLEN);
|
||||
|
||||
@@ -699,7 +724,12 @@ static void batadv_iv_ogm_aggregate_new(const unsigned char *packet_buff,
|
||||
send_time - jiffies);
|
||||
|
||||
return;
|
||||
out:
|
||||
out_free_forw_packet:
|
||||
kfree(forw_packet_aggr);
|
||||
out_nomem:
|
||||
if (!own_packet)
|
||||
atomic_inc(&bat_priv->batman_queue_left);
|
||||
out_free_outgoing:
|
||||
batadv_hardif_free_ref(if_outgoing);
|
||||
out_free_incoming:
|
||||
batadv_hardif_free_ref(if_incoming);
|
||||
@@ -752,13 +782,13 @@ static void batadv_iv_ogm_queue_add(struct batadv_priv *bat_priv,
|
||||
unsigned long max_aggregation_jiffies;
|
||||
|
||||
batadv_ogm_packet = (struct batadv_ogm_packet *)packet_buff;
|
||||
direct_link = batadv_ogm_packet->flags & BATADV_DIRECTLINK ? 1 : 0;
|
||||
direct_link = !!(batadv_ogm_packet->flags & BATADV_DIRECTLINK);
|
||||
max_aggregation_jiffies = msecs_to_jiffies(BATADV_MAX_AGGREGATION_MS);
|
||||
|
||||
/* find position for the packet in the forward queue */
|
||||
spin_lock_bh(&bat_priv->forw_bat_list_lock);
|
||||
/* own packets are not to be aggregated */
|
||||
if ((atomic_read(&bat_priv->aggregated_ogms)) && (!own_packet)) {
|
||||
if (atomic_read(&bat_priv->aggregated_ogms) && !own_packet) {
|
||||
hlist_for_each_entry(forw_packet_pos,
|
||||
&bat_priv->forw_bat_list, list) {
|
||||
if (batadv_iv_ogm_can_aggregate(batadv_ogm_packet,
|
||||
@@ -1034,9 +1064,10 @@ batadv_iv_ogm_orig_update(struct batadv_priv *bat_priv,
|
||||
batadv_orig_node_free_ref(orig_tmp);
|
||||
if (!neigh_node)
|
||||
goto unlock;
|
||||
} else
|
||||
} else {
|
||||
batadv_dbg(BATADV_DBG_BATMAN, bat_priv,
|
||||
"Updating existing last-hop neighbor of originator\n");
|
||||
}
|
||||
|
||||
rcu_read_unlock();
|
||||
neigh_ifinfo = batadv_neigh_ifinfo_new(neigh_node, if_outgoing);
|
||||
@@ -1081,7 +1112,7 @@ batadv_iv_ogm_orig_update(struct batadv_priv *bat_priv,
|
||||
* won't consider it either
|
||||
*/
|
||||
if (router_ifinfo &&
|
||||
(neigh_ifinfo->bat_iv.tq_avg == router_ifinfo->bat_iv.tq_avg)) {
|
||||
neigh_ifinfo->bat_iv.tq_avg == router_ifinfo->bat_iv.tq_avg) {
|
||||
orig_node_tmp = router->orig_node;
|
||||
spin_lock_bh(&orig_node_tmp->bat_iv.ogm_cnt_lock);
|
||||
if_num = router->if_incoming->if_num;
|
||||
@@ -1356,8 +1387,7 @@ 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);
|
||||
if (orig_ifinfo)
|
||||
batadv_orig_ifinfo_free_ref(orig_ifinfo);
|
||||
batadv_orig_ifinfo_free_ref(orig_ifinfo);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2006-2014 B.A.T.M.A.N. contributors:
|
||||
/* Copyright (C) 2006-2015 B.A.T.M.A.N. contributors:
|
||||
*
|
||||
* Simon Wunderlich, Marek Lindner
|
||||
*
|
||||
@@ -15,10 +15,10 @@
|
||||
* along with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "main.h"
|
||||
#include "bitarray.h"
|
||||
#include "main.h"
|
||||
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/bitmap.h>
|
||||
|
||||
/* shift the packet array by n places. */
|
||||
static void batadv_bitmap_shift_left(unsigned long *seq_bits, int32_t n)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2006-2014 B.A.T.M.A.N. contributors:
|
||||
/* Copyright (C) 2006-2015 B.A.T.M.A.N. contributors:
|
||||
*
|
||||
* Simon Wunderlich, Marek Lindner
|
||||
*
|
||||
@@ -18,6 +18,12 @@
|
||||
#ifndef _NET_BATMAN_ADV_BITARRAY_H_
|
||||
#define _NET_BATMAN_ADV_BITARRAY_H_
|
||||
|
||||
#include "main.h"
|
||||
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/compiler.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
/* Returns 1 if the corresponding bit in the given seq_bits indicates true
|
||||
* and curr_seqno is within range of last_seqno. Otherwise returns 0.
|
||||
*/
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2011-2014 B.A.T.M.A.N. contributors:
|
||||
/* Copyright (C) 2011-2015 B.A.T.M.A.N. contributors:
|
||||
*
|
||||
* Simon Wunderlich
|
||||
*
|
||||
@@ -15,19 +15,41 @@
|
||||
* along with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "main.h"
|
||||
#include "hash.h"
|
||||
#include "hard-interface.h"
|
||||
#include "originator.h"
|
||||
#include "bridge_loop_avoidance.h"
|
||||
#include "translation-table.h"
|
||||
#include "send.h"
|
||||
#include "main.h"
|
||||
|
||||
#include <linux/etherdevice.h>
|
||||
#include <linux/atomic.h>
|
||||
#include <linux/byteorder/generic.h>
|
||||
#include <linux/compiler.h>
|
||||
#include <linux/crc16.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/etherdevice.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/if_arp.h>
|
||||
#include <net/arp.h>
|
||||
#include <linux/if_ether.h>
|
||||
#include <linux/if_vlan.h>
|
||||
#include <linux/jhash.h>
|
||||
#include <linux/jiffies.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/lockdep.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/rculist.h>
|
||||
#include <linux/rcupdate.h>
|
||||
#include <linux/seq_file.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/stddef.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/workqueue.h>
|
||||
#include <net/arp.h>
|
||||
|
||||
#include "hard-interface.h"
|
||||
#include "hash.h"
|
||||
#include "originator.h"
|
||||
#include "packet.h"
|
||||
#include "translation-table.h"
|
||||
|
||||
static const uint8_t batadv_announce_mac[4] = {0x43, 0x05, 0x43, 0x05};
|
||||
|
||||
@@ -42,12 +64,8 @@ static inline uint32_t batadv_choose_claim(const void *data, uint32_t size)
|
||||
struct batadv_bla_claim *claim = (struct batadv_bla_claim *)data;
|
||||
uint32_t hash = 0;
|
||||
|
||||
hash = batadv_hash_bytes(hash, &claim->addr, sizeof(claim->addr));
|
||||
hash = batadv_hash_bytes(hash, &claim->vid, sizeof(claim->vid));
|
||||
|
||||
hash += (hash << 3);
|
||||
hash ^= (hash >> 11);
|
||||
hash += (hash << 15);
|
||||
hash = jhash(&claim->addr, sizeof(claim->addr), hash);
|
||||
hash = jhash(&claim->vid, sizeof(claim->vid), hash);
|
||||
|
||||
return hash % size;
|
||||
}
|
||||
@@ -59,12 +77,8 @@ static inline uint32_t batadv_choose_backbone_gw(const void *data,
|
||||
const struct batadv_bla_claim *claim = (struct batadv_bla_claim *)data;
|
||||
uint32_t hash = 0;
|
||||
|
||||
hash = batadv_hash_bytes(hash, &claim->addr, sizeof(claim->addr));
|
||||
hash = batadv_hash_bytes(hash, &claim->vid, sizeof(claim->vid));
|
||||
|
||||
hash += (hash << 3);
|
||||
hash ^= (hash >> 11);
|
||||
hash += (hash << 15);
|
||||
hash = jhash(&claim->addr, sizeof(claim->addr), hash);
|
||||
hash = jhash(&claim->vid, sizeof(claim->vid), hash);
|
||||
|
||||
return hash % size;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2011-2014 B.A.T.M.A.N. contributors:
|
||||
/* Copyright (C) 2011-2015 B.A.T.M.A.N. contributors:
|
||||
*
|
||||
* Simon Wunderlich
|
||||
*
|
||||
@@ -18,6 +18,16 @@
|
||||
#ifndef _NET_BATMAN_ADV_BLA_H_
|
||||
#define _NET_BATMAN_ADV_BLA_H_
|
||||
|
||||
#include "main.h"
|
||||
|
||||
#include <linux/types.h>
|
||||
|
||||
struct batadv_hard_iface;
|
||||
struct batadv_orig_node;
|
||||
struct batadv_priv;
|
||||
struct seq_file;
|
||||
struct sk_buff;
|
||||
|
||||
#ifdef CONFIG_BATMAN_ADV_BLA
|
||||
int batadv_bla_rx(struct batadv_priv *bat_priv, struct sk_buff *skb,
|
||||
unsigned short vid, bool is_bcast);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2010-2014 B.A.T.M.A.N. contributors:
|
||||
/* Copyright (C) 2010-2015 B.A.T.M.A.N. contributors:
|
||||
*
|
||||
* Marek Lindner
|
||||
*
|
||||
@@ -15,21 +15,42 @@
|
||||
* along with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "debugfs.h"
|
||||
#include "main.h"
|
||||
|
||||
#include <linux/compiler.h>
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/export.h>
|
||||
#include <linux/fcntl.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/jiffies.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/poll.h>
|
||||
#include <linux/printk.h>
|
||||
#include <linux/sched.h> /* for linux/wait.h */
|
||||
#include <linux/seq_file.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/stat.h>
|
||||
#include <linux/stddef.h>
|
||||
#include <linux/stringify.h>
|
||||
#include <linux/sysfs.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <linux/wait.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
#include "debugfs.h"
|
||||
#include "translation-table.h"
|
||||
#include "originator.h"
|
||||
#include "hard-interface.h"
|
||||
#include "gateway_common.h"
|
||||
#include "gateway_client.h"
|
||||
#include "soft-interface.h"
|
||||
#include "icmp_socket.h"
|
||||
#include "bridge_loop_avoidance.h"
|
||||
#include "distributed-arp-table.h"
|
||||
#include "gateway_client.h"
|
||||
#include "icmp_socket.h"
|
||||
#include "network-coding.h"
|
||||
#include "originator.h"
|
||||
#include "translation-table.h"
|
||||
|
||||
static struct dentry *batadv_debugfs;
|
||||
|
||||
@@ -482,11 +503,7 @@ int batadv_debugfs_add_hardif(struct batadv_hard_iface *hard_iface)
|
||||
debugfs_remove_recursive(hard_iface->debug_dir);
|
||||
hard_iface->debug_dir = NULL;
|
||||
out:
|
||||
#ifdef CONFIG_DEBUG_FS
|
||||
return -ENOMEM;
|
||||
#else
|
||||
return 0;
|
||||
#endif /* CONFIG_DEBUG_FS */
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -541,11 +558,7 @@ int batadv_debugfs_add_meshif(struct net_device *dev)
|
||||
debugfs_remove_recursive(bat_priv->debug_dir);
|
||||
bat_priv->debug_dir = NULL;
|
||||
out:
|
||||
#ifdef CONFIG_DEBUG_FS
|
||||
return -ENOMEM;
|
||||
#else
|
||||
return 0;
|
||||
#endif /* CONFIG_DEBUG_FS */
|
||||
}
|
||||
|
||||
void batadv_debugfs_del_meshif(struct net_device *dev)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2010-2014 B.A.T.M.A.N. contributors:
|
||||
/* Copyright (C) 2010-2015 B.A.T.M.A.N. contributors:
|
||||
*
|
||||
* Marek Lindner
|
||||
*
|
||||
@@ -18,8 +18,17 @@
|
||||
#ifndef _NET_BATMAN_ADV_DEBUGFS_H_
|
||||
#define _NET_BATMAN_ADV_DEBUGFS_H_
|
||||
|
||||
#include "main.h"
|
||||
|
||||
#include <linux/kconfig.h>
|
||||
|
||||
struct batadv_hard_iface;
|
||||
struct net_device;
|
||||
|
||||
#define BATADV_DEBUGFS_SUBDIR "batman_adv"
|
||||
|
||||
#if IS_ENABLED(CONFIG_DEBUG_FS)
|
||||
|
||||
void batadv_debugfs_init(void);
|
||||
void batadv_debugfs_destroy(void);
|
||||
int batadv_debugfs_add_meshif(struct net_device *dev);
|
||||
@@ -27,4 +36,36 @@ void batadv_debugfs_del_meshif(struct net_device *dev);
|
||||
int batadv_debugfs_add_hardif(struct batadv_hard_iface *hard_iface);
|
||||
void batadv_debugfs_del_hardif(struct batadv_hard_iface *hard_iface);
|
||||
|
||||
#else
|
||||
|
||||
static inline void batadv_debugfs_init(void)
|
||||
{
|
||||
}
|
||||
|
||||
static inline void batadv_debugfs_destroy(void)
|
||||
{
|
||||
}
|
||||
|
||||
static inline int batadv_debugfs_add_meshif(struct net_device *dev)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void batadv_debugfs_del_meshif(struct net_device *dev)
|
||||
{
|
||||
}
|
||||
|
||||
static inline
|
||||
int batadv_debugfs_add_hardif(struct batadv_hard_iface *hard_iface)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline
|
||||
void batadv_debugfs_del_hardif(struct batadv_hard_iface *hard_iface)
|
||||
{
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#endif /* _NET_BATMAN_ADV_DEBUGFS_H_ */
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2011-2014 B.A.T.M.A.N. contributors:
|
||||
/* Copyright (C) 2011-2015 B.A.T.M.A.N. contributors:
|
||||
*
|
||||
* Antonio Quartulli
|
||||
*
|
||||
@@ -15,18 +15,36 @@
|
||||
* along with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <linux/if_ether.h>
|
||||
#include "distributed-arp-table.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_arp.h>
|
||||
#include <linux/if_ether.h>
|
||||
#include <linux/if_vlan.h>
|
||||
#include <linux/in.h>
|
||||
#include <linux/jiffies.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/rculist.h>
|
||||
#include <linux/rcupdate.h>
|
||||
#include <linux/seq_file.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/stddef.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/workqueue.h>
|
||||
#include <net/arp.h>
|
||||
|
||||
#include "main.h"
|
||||
#include "hash.h"
|
||||
#include "distributed-arp-table.h"
|
||||
#include "hard-interface.h"
|
||||
#include "hash.h"
|
||||
#include "originator.h"
|
||||
#include "send.h"
|
||||
#include "types.h"
|
||||
#include "translation-table.h"
|
||||
|
||||
static void batadv_dat_purge(struct work_struct *work);
|
||||
@@ -206,9 +224,22 @@ static uint32_t batadv_hash_dat(const void *data, uint32_t size)
|
||||
{
|
||||
uint32_t hash = 0;
|
||||
const struct batadv_dat_entry *dat = data;
|
||||
const unsigned char *key;
|
||||
uint32_t i;
|
||||
|
||||
hash = batadv_hash_bytes(hash, &dat->ip, sizeof(dat->ip));
|
||||
hash = batadv_hash_bytes(hash, &dat->vid, sizeof(dat->vid));
|
||||
key = (const unsigned char *)&dat->ip;
|
||||
for (i = 0; i < sizeof(dat->ip); i++) {
|
||||
hash += key[i];
|
||||
hash += (hash << 10);
|
||||
hash ^= (hash >> 6);
|
||||
}
|
||||
|
||||
key = (const unsigned char *)&dat->vid;
|
||||
for (i = 0; i < sizeof(dat->vid); i++) {
|
||||
hash += key[i];
|
||||
hash += (hash << 10);
|
||||
hash ^= (hash >> 6);
|
||||
}
|
||||
|
||||
hash += (hash << 3);
|
||||
hash ^= (hash >> 11);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2011-2014 B.A.T.M.A.N. contributors:
|
||||
/* Copyright (C) 2011-2015 B.A.T.M.A.N. contributors:
|
||||
*
|
||||
* Antonio Quartulli
|
||||
*
|
||||
@@ -18,12 +18,19 @@
|
||||
#ifndef _NET_BATMAN_ADV_DISTRIBUTED_ARP_TABLE_H_
|
||||
#define _NET_BATMAN_ADV_DISTRIBUTED_ARP_TABLE_H_
|
||||
|
||||
#ifdef CONFIG_BATMAN_ADV_DAT
|
||||
#include "main.h"
|
||||
|
||||
#include <linux/compiler.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
#include "types.h"
|
||||
#include "originator.h"
|
||||
#include "packet.h"
|
||||
|
||||
#include <linux/if_arp.h>
|
||||
struct seq_file;
|
||||
struct sk_buff;
|
||||
|
||||
#ifdef CONFIG_BATMAN_ADV_DAT
|
||||
|
||||
/* BATADV_DAT_ADDR_MAX - maximum address value in the DHT space */
|
||||
#define BATADV_DAT_ADDR_MAX ((batadv_dat_addr_t)~(batadv_dat_addr_t)0)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2013-2014 B.A.T.M.A.N. contributors:
|
||||
/* Copyright (C) 2013-2015 B.A.T.M.A.N. contributors:
|
||||
*
|
||||
* Martin Hundebøll <martin@hundeboll.net>
|
||||
*
|
||||
@@ -15,12 +15,28 @@
|
||||
* along with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "main.h"
|
||||
#include "fragmentation.h"
|
||||
#include "send.h"
|
||||
#include "originator.h"
|
||||
#include "routing.h"
|
||||
#include "main.h"
|
||||
|
||||
#include <linux/atomic.h>
|
||||
#include <linux/byteorder/generic.h>
|
||||
#include <linux/etherdevice.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/if_ether.h>
|
||||
#include <linux/jiffies.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/pkt_sched.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/string.h>
|
||||
|
||||
#include "hard-interface.h"
|
||||
#include "originator.h"
|
||||
#include "packet.h"
|
||||
#include "routing.h"
|
||||
#include "send.h"
|
||||
#include "soft-interface.h"
|
||||
|
||||
/**
|
||||
@@ -161,6 +177,7 @@ static bool batadv_frag_insert_packet(struct batadv_orig_node *orig_node,
|
||||
hlist_add_head(&frag_entry_new->list, &chain->head);
|
||||
chain->size = skb->len - hdr_size;
|
||||
chain->timestamp = jiffies;
|
||||
chain->total_size = ntohs(frag_packet->total_size);
|
||||
ret = true;
|
||||
goto out;
|
||||
}
|
||||
@@ -195,9 +212,11 @@ static bool batadv_frag_insert_packet(struct batadv_orig_node *orig_node,
|
||||
|
||||
out:
|
||||
if (chain->size > batadv_frag_size_limit() ||
|
||||
ntohs(frag_packet->total_size) > batadv_frag_size_limit()) {
|
||||
chain->total_size != ntohs(frag_packet->total_size) ||
|
||||
chain->total_size > batadv_frag_size_limit()) {
|
||||
/* Clear chain if total size of either the list or the packet
|
||||
* exceeds the maximum size of one merged packet.
|
||||
* exceeds the maximum size of one merged packet. Don't allow
|
||||
* packets to have different total_size.
|
||||
*/
|
||||
batadv_frag_clear_chain(&chain->head);
|
||||
chain->size = 0;
|
||||
@@ -228,19 +247,13 @@ static bool batadv_frag_insert_packet(struct batadv_orig_node *orig_node,
|
||||
* Returns the merged skb or NULL on error.
|
||||
*/
|
||||
static struct sk_buff *
|
||||
batadv_frag_merge_packets(struct hlist_head *chain, struct sk_buff *skb)
|
||||
batadv_frag_merge_packets(struct hlist_head *chain)
|
||||
{
|
||||
struct batadv_frag_packet *packet;
|
||||
struct batadv_frag_list_entry *entry;
|
||||
struct sk_buff *skb_out = NULL;
|
||||
int size, hdr_size = sizeof(struct batadv_frag_packet);
|
||||
|
||||
/* Make sure incoming skb has non-bogus data. */
|
||||
packet = (struct batadv_frag_packet *)skb->data;
|
||||
size = ntohs(packet->total_size);
|
||||
if (size > batadv_frag_size_limit())
|
||||
goto free;
|
||||
|
||||
/* Remove first entry, as this is the destination for the rest of the
|
||||
* fragments.
|
||||
*/
|
||||
@@ -249,6 +262,9 @@ batadv_frag_merge_packets(struct hlist_head *chain, struct sk_buff *skb)
|
||||
skb_out = entry->skb;
|
||||
kfree(entry);
|
||||
|
||||
packet = (struct batadv_frag_packet *)skb_out->data;
|
||||
size = ntohs(packet->total_size);
|
||||
|
||||
/* Make room for the rest of the fragments. */
|
||||
if (pskb_expand_head(skb_out, 0, size - skb_out->len, GFP_ATOMIC) < 0) {
|
||||
kfree_skb(skb_out);
|
||||
@@ -304,7 +320,7 @@ bool batadv_frag_skb_buffer(struct sk_buff **skb,
|
||||
if (hlist_empty(&head))
|
||||
goto out;
|
||||
|
||||
skb_out = batadv_frag_merge_packets(&head, *skb);
|
||||
skb_out = batadv_frag_merge_packets(&head);
|
||||
if (!skb_out)
|
||||
goto out_err;
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2013-2014 B.A.T.M.A.N. contributors:
|
||||
/* Copyright (C) 2013-2015 B.A.T.M.A.N. contributors:
|
||||
*
|
||||
* Martin Hundebøll <martin@hundeboll.net>
|
||||
*
|
||||
@@ -18,6 +18,15 @@
|
||||
#ifndef _NET_BATMAN_ADV_FRAGMENTATION_H_
|
||||
#define _NET_BATMAN_ADV_FRAGMENTATION_H_
|
||||
|
||||
#include "main.h"
|
||||
|
||||
#include <linux/compiler.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/stddef.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
struct sk_buff;
|
||||
|
||||
void batadv_frag_purge_orig(struct batadv_orig_node *orig,
|
||||
bool (*check_cb)(struct batadv_frag_table_entry *));
|
||||
bool batadv_frag_skb_fwd(struct sk_buff *skb,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2009-2014 B.A.T.M.A.N. contributors:
|
||||
/* Copyright (C) 2009-2015 B.A.T.M.A.N. contributors:
|
||||
*
|
||||
* Marek Lindner
|
||||
*
|
||||
@@ -15,18 +15,38 @@
|
||||
* along with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "main.h"
|
||||
#include "sysfs.h"
|
||||
#include "gateway_client.h"
|
||||
#include "main.h"
|
||||
|
||||
#include <linux/atomic.h>
|
||||
#include <linux/byteorder/generic.h>
|
||||
#include <linux/etherdevice.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/if_ether.h>
|
||||
#include <linux/if_vlan.h>
|
||||
#include <linux/in.h>
|
||||
#include <linux/ip.h>
|
||||
#include <linux/ipv6.h>
|
||||
#include <linux/jiffies.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/rculist.h>
|
||||
#include <linux/rcupdate.h>
|
||||
#include <linux/seq_file.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/stddef.h>
|
||||
#include <linux/udp.h>
|
||||
|
||||
#include "gateway_common.h"
|
||||
#include "hard-interface.h"
|
||||
#include "originator.h"
|
||||
#include "translation-table.h"
|
||||
#include "packet.h"
|
||||
#include "routing.h"
|
||||
#include <linux/ip.h>
|
||||
#include <linux/ipv6.h>
|
||||
#include <linux/udp.h>
|
||||
#include <linux/if_vlan.h>
|
||||
#include "sysfs.h"
|
||||
#include "translation-table.h"
|
||||
|
||||
/* These are the offsets of the "hw type" and "hw address length" in the dhcp
|
||||
* packet starting at the beginning of the dhcp header
|
||||
@@ -733,11 +753,6 @@ batadv_gw_dhcp_recipient_get(struct sk_buff *skb, unsigned int *header_len,
|
||||
if (!pskb_may_pull(skb, *header_len + sizeof(*udphdr)))
|
||||
return BATADV_DHCP_NO;
|
||||
|
||||
/* skb->data might have been reallocated by pskb_may_pull() */
|
||||
ethhdr = eth_hdr(skb);
|
||||
if (ntohs(ethhdr->h_proto) == ETH_P_8021Q)
|
||||
ethhdr = (struct ethhdr *)(skb->data + VLAN_HLEN);
|
||||
|
||||
udphdr = (struct udphdr *)(skb->data + *header_len);
|
||||
*header_len += sizeof(*udphdr);
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2009-2014 B.A.T.M.A.N. contributors:
|
||||
/* Copyright (C) 2009-2015 B.A.T.M.A.N. contributors:
|
||||
*
|
||||
* Marek Lindner
|
||||
*
|
||||
@@ -18,6 +18,14 @@
|
||||
#ifndef _NET_BATMAN_ADV_GATEWAY_CLIENT_H_
|
||||
#define _NET_BATMAN_ADV_GATEWAY_CLIENT_H_
|
||||
|
||||
#include "main.h"
|
||||
|
||||
#include <linux/types.h>
|
||||
|
||||
struct batadv_tvlv_gateway_data;
|
||||
struct seq_file;
|
||||
struct sk_buff;
|
||||
|
||||
void batadv_gw_check_client_stop(struct batadv_priv *bat_priv);
|
||||
void batadv_gw_reselect(struct batadv_priv *bat_priv);
|
||||
void batadv_gw_election(struct batadv_priv *bat_priv);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2009-2014 B.A.T.M.A.N. contributors:
|
||||
/* Copyright (C) 2009-2015 B.A.T.M.A.N. contributors:
|
||||
*
|
||||
* Marek Lindner
|
||||
*
|
||||
@@ -15,9 +15,18 @@
|
||||
* along with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "main.h"
|
||||
#include "gateway_common.h"
|
||||
#include "main.h"
|
||||
|
||||
#include <linux/atomic.h>
|
||||
#include <linux/byteorder/generic.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/stddef.h>
|
||||
#include <linux/string.h>
|
||||
|
||||
#include "gateway_client.h"
|
||||
#include "packet.h"
|
||||
|
||||
/**
|
||||
* batadv_parse_gw_bandwidth - parse supplied string buffer to extract download
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2009-2014 B.A.T.M.A.N. contributors:
|
||||
/* Copyright (C) 2009-2015 B.A.T.M.A.N. contributors:
|
||||
*
|
||||
* Marek Lindner
|
||||
*
|
||||
@@ -18,6 +18,13 @@
|
||||
#ifndef _NET_BATMAN_ADV_GATEWAY_COMMON_H_
|
||||
#define _NET_BATMAN_ADV_GATEWAY_COMMON_H_
|
||||
|
||||
#include "main.h"
|
||||
|
||||
#include <linux/types.h>
|
||||
|
||||
struct batadv_priv;
|
||||
struct net_device;
|
||||
|
||||
enum batadv_gw_modes {
|
||||
BATADV_GW_MODE_OFF,
|
||||
BATADV_GW_MODE_CLIENT,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2007-2014 B.A.T.M.A.N. contributors:
|
||||
/* Copyright (C) 2007-2015 B.A.T.M.A.N. contributors:
|
||||
*
|
||||
* Marek Lindner, Simon Wunderlich
|
||||
*
|
||||
@@ -15,22 +15,36 @@
|
||||
* along with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "main.h"
|
||||
#include "distributed-arp-table.h"
|
||||
#include "hard-interface.h"
|
||||
#include "soft-interface.h"
|
||||
#include "send.h"
|
||||
#include "translation-table.h"
|
||||
#include "routing.h"
|
||||
#include "sysfs.h"
|
||||
#include "debugfs.h"
|
||||
#include "originator.h"
|
||||
#include "hash.h"
|
||||
#include "bridge_loop_avoidance.h"
|
||||
#include "gateway_client.h"
|
||||
#include "main.h"
|
||||
|
||||
#include <linux/bug.h>
|
||||
#include <linux/byteorder/generic.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/if_arp.h>
|
||||
#include <linux/if_ether.h>
|
||||
#include <linux/if.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/printk.h>
|
||||
#include <linux/rculist.h>
|
||||
#include <linux/rtnetlink.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/workqueue.h>
|
||||
#include <net/net_namespace.h>
|
||||
|
||||
#include "bridge_loop_avoidance.h"
|
||||
#include "debugfs.h"
|
||||
#include "distributed-arp-table.h"
|
||||
#include "gateway_client.h"
|
||||
#include "originator.h"
|
||||
#include "packet.h"
|
||||
#include "send.h"
|
||||
#include "soft-interface.h"
|
||||
#include "sysfs.h"
|
||||
#include "translation-table.h"
|
||||
|
||||
void batadv_hardif_free_rcu(struct rcu_head *rcu)
|
||||
{
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2007-2014 B.A.T.M.A.N. contributors:
|
||||
/* Copyright (C) 2007-2015 B.A.T.M.A.N. contributors:
|
||||
*
|
||||
* Marek Lindner, Simon Wunderlich
|
||||
*
|
||||
@@ -18,6 +18,17 @@
|
||||
#ifndef _NET_BATMAN_ADV_HARD_INTERFACE_H_
|
||||
#define _NET_BATMAN_ADV_HARD_INTERFACE_H_
|
||||
|
||||
#include "main.h"
|
||||
|
||||
#include <linux/atomic.h>
|
||||
#include <linux/compiler.h>
|
||||
#include <linux/notifier.h>
|
||||
#include <linux/rcupdate.h>
|
||||
#include <linux/stddef.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
struct net_device;
|
||||
|
||||
enum batadv_hard_if_state {
|
||||
BATADV_IF_NOT_IN_USE,
|
||||
BATADV_IF_TO_BE_REMOVED,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2006-2014 B.A.T.M.A.N. contributors:
|
||||
/* Copyright (C) 2006-2015 B.A.T.M.A.N. contributors:
|
||||
*
|
||||
* Simon Wunderlich, Marek Lindner
|
||||
*
|
||||
@@ -15,8 +15,12 @@
|
||||
* along with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "main.h"
|
||||
#include "hash.h"
|
||||
#include "main.h"
|
||||
|
||||
#include <linux/fs.h>
|
||||
#include <linux/lockdep.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
/* clears the hash */
|
||||
static void batadv_hash_init(struct batadv_hashtable *hash)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2006-2014 B.A.T.M.A.N. contributors:
|
||||
/* Copyright (C) 2006-2015 B.A.T.M.A.N. contributors:
|
||||
*
|
||||
* Simon Wunderlich, Marek Lindner
|
||||
*
|
||||
@@ -18,7 +18,16 @@
|
||||
#ifndef _NET_BATMAN_ADV_HASH_H_
|
||||
#define _NET_BATMAN_ADV_HASH_H_
|
||||
|
||||
#include "main.h"
|
||||
|
||||
#include <linux/compiler.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/rculist.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/stddef.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
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
|
||||
@@ -79,28 +88,6 @@ static inline void batadv_hash_delete(struct batadv_hashtable *hash,
|
||||
batadv_hash_destroy(hash);
|
||||
}
|
||||
|
||||
/**
|
||||
* batadv_hash_bytes - hash some bytes and add them to the previous hash
|
||||
* @hash: previous hash value
|
||||
* @data: data to be hashed
|
||||
* @size: number of bytes to be hashed
|
||||
*
|
||||
* Returns the new hash value.
|
||||
*/
|
||||
static inline uint32_t batadv_hash_bytes(uint32_t hash, const void *data,
|
||||
uint32_t size)
|
||||
{
|
||||
const unsigned char *key = data;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < size; i++) {
|
||||
hash += key[i];
|
||||
hash += (hash << 10);
|
||||
hash ^= (hash >> 6);
|
||||
}
|
||||
return hash;
|
||||
}
|
||||
|
||||
/**
|
||||
* batadv_hash_add - adds data to the hashtable
|
||||
* @hash: storage hash table
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2007-2014 B.A.T.M.A.N. contributors:
|
||||
/* Copyright (C) 2007-2015 B.A.T.M.A.N. contributors:
|
||||
*
|
||||
* Marek Lindner
|
||||
*
|
||||
@@ -15,14 +15,39 @@
|
||||
* along with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "main.h"
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/slab.h>
|
||||
#include "icmp_socket.h"
|
||||
#include "send.h"
|
||||
#include "hash.h"
|
||||
#include "originator.h"
|
||||
#include "main.h"
|
||||
|
||||
#include <linux/atomic.h>
|
||||
#include <linux/compiler.h>
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/etherdevice.h>
|
||||
#include <linux/export.h>
|
||||
#include <linux/fcntl.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/if_ether.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/pkt_sched.h>
|
||||
#include <linux/poll.h>
|
||||
#include <linux/printk.h>
|
||||
#include <linux/sched.h> /* for linux/wait.h */
|
||||
#include <linux/skbuff.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/stat.h>
|
||||
#include <linux/stddef.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <linux/wait.h>
|
||||
|
||||
#include "hard-interface.h"
|
||||
#include "originator.h"
|
||||
#include "packet.h"
|
||||
#include "send.h"
|
||||
|
||||
static struct batadv_socket_client *batadv_socket_client_hash[256];
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2007-2014 B.A.T.M.A.N. contributors:
|
||||
/* Copyright (C) 2007-2015 B.A.T.M.A.N. contributors:
|
||||
*
|
||||
* Marek Lindner
|
||||
*
|
||||
@@ -18,6 +18,13 @@
|
||||
#ifndef _NET_BATMAN_ADV_ICMP_SOCKET_H_
|
||||
#define _NET_BATMAN_ADV_ICMP_SOCKET_H_
|
||||
|
||||
#include "main.h"
|
||||
|
||||
#include <linux/types.h>
|
||||
|
||||
struct batadv_icmp_header;
|
||||
struct batadv_priv;
|
||||
|
||||
#define BATADV_ICMP_SOCKET "socket"
|
||||
|
||||
void batadv_socket_init(void);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2007-2014 B.A.T.M.A.N. contributors:
|
||||
/* Copyright (C) 2007-2015 B.A.T.M.A.N. contributors:
|
||||
*
|
||||
* Marek Lindner, Simon Wunderlich
|
||||
*
|
||||
@@ -15,31 +15,53 @@
|
||||
* along with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <linux/crc32c.h>
|
||||
#include <linux/highmem.h>
|
||||
#include <linux/if_vlan.h>
|
||||
#include <net/ip.h>
|
||||
#include <net/ipv6.h>
|
||||
#include <net/dsfield.h>
|
||||
#include "main.h"
|
||||
#include "sysfs.h"
|
||||
|
||||
#include <linux/atomic.h>
|
||||
#include <linux/bug.h>
|
||||
#include <linux/byteorder/generic.h>
|
||||
#include <linux/crc32c.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/if_ether.h>
|
||||
#include <linux/if_vlan.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/ip.h>
|
||||
#include <linux/ipv6.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/moduleparam.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/pkt_sched.h>
|
||||
#include <linux/rculist.h>
|
||||
#include <linux/rcupdate.h>
|
||||
#include <linux/seq_file.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/stddef.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/workqueue.h>
|
||||
#include <net/dsfield.h>
|
||||
#include <net/rtnetlink.h>
|
||||
|
||||
#include "bat_algo.h"
|
||||
#include "bridge_loop_avoidance.h"
|
||||
#include "debugfs.h"
|
||||
#include "distributed-arp-table.h"
|
||||
#include "gateway_client.h"
|
||||
#include "gateway_common.h"
|
||||
#include "hard-interface.h"
|
||||
#include "icmp_socket.h"
|
||||
#include "multicast.h"
|
||||
#include "network-coding.h"
|
||||
#include "originator.h"
|
||||
#include "packet.h"
|
||||
#include "routing.h"
|
||||
#include "send.h"
|
||||
#include "originator.h"
|
||||
#include "soft-interface.h"
|
||||
#include "icmp_socket.h"
|
||||
#include "translation-table.h"
|
||||
#include "hard-interface.h"
|
||||
#include "gateway_client.h"
|
||||
#include "bridge_loop_avoidance.h"
|
||||
#include "distributed-arp-table.h"
|
||||
#include "multicast.h"
|
||||
#include "gateway_common.h"
|
||||
#include "hash.h"
|
||||
#include "bat_algo.h"
|
||||
#include "network-coding.h"
|
||||
#include "fragmentation.h"
|
||||
|
||||
/* List manipulations on hardif_list have to be rtnl_lock()'ed,
|
||||
* list traversals just rcu-locked
|
||||
@@ -209,10 +231,13 @@ void batadv_mesh_free(struct net_device *soft_iface)
|
||||
* interfaces in the current mesh
|
||||
* @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.
|
||||
*/
|
||||
int batadv_is_my_mac(struct batadv_priv *bat_priv, const uint8_t *addr)
|
||||
bool batadv_is_my_mac(struct batadv_priv *bat_priv, const uint8_t *addr)
|
||||
{
|
||||
const struct batadv_hard_iface *hard_iface;
|
||||
bool is_my_mac = false;
|
||||
|
||||
rcu_read_lock();
|
||||
list_for_each_entry_rcu(hard_iface, &batadv_hardif_list, list) {
|
||||
@@ -223,12 +248,12 @@ int batadv_is_my_mac(struct batadv_priv *bat_priv, const uint8_t *addr)
|
||||
continue;
|
||||
|
||||
if (batadv_compare_eth(hard_iface->net_dev->dev_addr, addr)) {
|
||||
rcu_read_unlock();
|
||||
return 1;
|
||||
is_my_mac = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
rcu_read_unlock();
|
||||
return 0;
|
||||
return is_my_mac;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -510,14 +535,12 @@ static struct batadv_algo_ops *batadv_algo_get(char *name)
|
||||
int batadv_algo_register(struct batadv_algo_ops *bat_algo_ops)
|
||||
{
|
||||
struct batadv_algo_ops *bat_algo_ops_tmp;
|
||||
int ret;
|
||||
|
||||
bat_algo_ops_tmp = batadv_algo_get(bat_algo_ops->name);
|
||||
if (bat_algo_ops_tmp) {
|
||||
pr_info("Trying to register already registered routing algorithm: %s\n",
|
||||
bat_algo_ops->name);
|
||||
ret = -EEXIST;
|
||||
goto out;
|
||||
return -EEXIST;
|
||||
}
|
||||
|
||||
/* all algorithms must implement all ops (for now) */
|
||||
@@ -531,32 +554,26 @@ int batadv_algo_register(struct batadv_algo_ops *bat_algo_ops)
|
||||
!bat_algo_ops->bat_neigh_is_equiv_or_better) {
|
||||
pr_info("Routing algo '%s' does not implement required ops\n",
|
||||
bat_algo_ops->name);
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
INIT_HLIST_NODE(&bat_algo_ops->list);
|
||||
hlist_add_head(&bat_algo_ops->list, &batadv_algo_list);
|
||||
ret = 0;
|
||||
|
||||
out:
|
||||
return ret;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int batadv_algo_select(struct batadv_priv *bat_priv, char *name)
|
||||
{
|
||||
struct batadv_algo_ops *bat_algo_ops;
|
||||
int ret = -EINVAL;
|
||||
|
||||
bat_algo_ops = batadv_algo_get(name);
|
||||
if (!bat_algo_ops)
|
||||
goto out;
|
||||
return -EINVAL;
|
||||
|
||||
bat_priv->bat_algo_ops = bat_algo_ops;
|
||||
ret = 0;
|
||||
|
||||
out:
|
||||
return ret;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int batadv_algo_seq_print_text(struct seq_file *seq, void *offset)
|
||||
@@ -819,15 +836,15 @@ static bool batadv_tvlv_realloc_packet_buff(unsigned char **packet_buff,
|
||||
new_buff = kmalloc(min_packet_len + additional_packet_len, GFP_ATOMIC);
|
||||
|
||||
/* keep old buffer if kmalloc should fail */
|
||||
if (new_buff) {
|
||||
memcpy(new_buff, *packet_buff, min_packet_len);
|
||||
kfree(*packet_buff);
|
||||
*packet_buff = new_buff;
|
||||
*packet_buff_len = min_packet_len + additional_packet_len;
|
||||
return true;
|
||||
}
|
||||
if (!new_buff)
|
||||
return false;
|
||||
|
||||
return false;
|
||||
memcpy(new_buff, *packet_buff, min_packet_len);
|
||||
kfree(*packet_buff);
|
||||
*packet_buff = new_buff;
|
||||
*packet_buff_len = min_packet_len + additional_packet_len;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2007-2014 B.A.T.M.A.N. contributors:
|
||||
/* Copyright (C) 2007-2015 B.A.T.M.A.N. contributors:
|
||||
*
|
||||
* Marek Lindner, Simon Wunderlich
|
||||
*
|
||||
@@ -24,7 +24,7 @@
|
||||
#define BATADV_DRIVER_DEVICE "batman-adv"
|
||||
|
||||
#ifndef BATADV_SOURCE_VERSION
|
||||
#define BATADV_SOURCE_VERSION "2015.0"
|
||||
#define BATADV_SOURCE_VERSION "2015.1"
|
||||
#endif
|
||||
|
||||
/* B.A.T.M.A.N. parameters */
|
||||
@@ -44,7 +44,7 @@
|
||||
#define BATADV_TT_CLIENT_TEMP_TIMEOUT 600000 /* in milliseconds */
|
||||
#define BATADV_TT_WORK_PERIOD 5000 /* 5 seconds */
|
||||
#define BATADV_ORIG_WORK_PERIOD 1000 /* 1 second */
|
||||
#define BATADV_DAT_ENTRY_TIMEOUT (5*60000) /* 5 mins in milliseconds */
|
||||
#define BATADV_DAT_ENTRY_TIMEOUT (5 * 60000) /* 5 mins in milliseconds */
|
||||
/* sliding packet range of received originator messages in sequence numbers
|
||||
* (should be a multiple of our word size)
|
||||
*/
|
||||
@@ -163,28 +163,26 @@ enum batadv_uev_type {
|
||||
|
||||
/* Kernel headers */
|
||||
|
||||
#include <linux/mutex.h> /* mutex */
|
||||
#include <linux/module.h> /* needed by all modules */
|
||||
#include <linux/netdevice.h> /* netdevice */
|
||||
#include <linux/etherdevice.h> /* ethernet address classification */
|
||||
#include <linux/if_ether.h> /* ethernet header */
|
||||
#include <linux/poll.h> /* poll_table */
|
||||
#include <linux/kthread.h> /* kernel threads */
|
||||
#include <linux/pkt_sched.h> /* schedule types */
|
||||
#include <linux/workqueue.h> /* workqueue */
|
||||
#include <linux/atomic.h>
|
||||
#include <linux/bitops.h> /* for packet.h */
|
||||
#include <linux/compiler.h>
|
||||
#include <linux/cpumask.h>
|
||||
#include <linux/etherdevice.h>
|
||||
#include <linux/if_ether.h> /* for packet.h */
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/printk.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/percpu.h>
|
||||
#include <linux/slab.h>
|
||||
#include <net/sock.h> /* struct sock */
|
||||
#include <net/addrconf.h> /* ipv6 address stuff */
|
||||
#include <linux/ip.h>
|
||||
#include <net/rtnetlink.h>
|
||||
#include <linux/jiffies.h>
|
||||
#include <linux/seq_file.h>
|
||||
#include <linux/if_vlan.h>
|
||||
|
||||
#include "types.h"
|
||||
|
||||
#define BATADV_PRINT_VID(vid) (vid & BATADV_VLAN_HAS_TAG ? \
|
||||
struct batadv_ogm_packet;
|
||||
struct seq_file;
|
||||
struct sk_buff;
|
||||
|
||||
#define BATADV_PRINT_VID(vid) ((vid & BATADV_VLAN_HAS_TAG) ? \
|
||||
(int)(vid & VLAN_VID_MASK) : -1)
|
||||
|
||||
extern char batadv_routing_algo[];
|
||||
@@ -195,7 +193,7 @@ extern struct workqueue_struct *batadv_event_workqueue;
|
||||
|
||||
int batadv_mesh_init(struct net_device *soft_iface);
|
||||
void batadv_mesh_free(struct net_device *soft_iface);
|
||||
int batadv_is_my_mac(struct batadv_priv *bat_priv, const uint8_t *addr);
|
||||
bool batadv_is_my_mac(struct batadv_priv *bat_priv, const uint8_t *addr);
|
||||
struct batadv_hard_iface *
|
||||
batadv_seq_print_text_primary_if_get(struct seq_file *seq);
|
||||
int batadv_max_header_len(void);
|
||||
@@ -279,7 +277,7 @@ static inline void _batadv_dbg(int type __always_unused,
|
||||
*
|
||||
* note: can't use ether_addr_equal() as it requires aligned memory
|
||||
*/
|
||||
static inline int batadv_compare_eth(const void *data1, const void *data2)
|
||||
static inline bool batadv_compare_eth(const void *data1, const void *data2)
|
||||
{
|
||||
return ether_addr_equal_unaligned(data1, data2);
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2014 B.A.T.M.A.N. contributors:
|
||||
/* Copyright (C) 2014-2015 B.A.T.M.A.N. contributors:
|
||||
*
|
||||
* Linus Lüssing
|
||||
*
|
||||
@@ -15,10 +15,33 @@
|
||||
* along with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "main.h"
|
||||
#include "multicast.h"
|
||||
#include "originator.h"
|
||||
#include "hard-interface.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/in6.h>
|
||||
#include <linux/in.h>
|
||||
#include <linux/ip.h>
|
||||
#include <linux/ipv6.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/rculist.h>
|
||||
#include <linux/rcupdate.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/stddef.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/types.h>
|
||||
#include <net/addrconf.h>
|
||||
#include <net/ipv6.h>
|
||||
|
||||
#include "packet.h"
|
||||
#include "translation-table.h"
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2014 B.A.T.M.A.N. contributors:
|
||||
/* Copyright (C) 2014-2015 B.A.T.M.A.N. contributors:
|
||||
*
|
||||
* Linus Lüssing
|
||||
*
|
||||
@@ -18,6 +18,12 @@
|
||||
#ifndef _NET_BATMAN_ADV_MULTICAST_H_
|
||||
#define _NET_BATMAN_ADV_MULTICAST_H_
|
||||
|
||||
#include "main.h"
|
||||
|
||||
struct batadv_orig_node;
|
||||
struct batadv_priv;
|
||||
struct sk_buff;
|
||||
|
||||
/**
|
||||
* batadv_forw_mode - the way a packet should be forwarded as
|
||||
* @BATADV_FORW_ALL: forward the packet to all nodes (currently via classic
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2012-2014 B.A.T.M.A.N. contributors:
|
||||
/* Copyright (C) 2012-2015 B.A.T.M.A.N. contributors:
|
||||
*
|
||||
* Martin Hundebøll, Jeppe Ledet-Pedersen
|
||||
*
|
||||
@@ -15,15 +15,44 @@
|
||||
* along with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <linux/debugfs.h>
|
||||
|
||||
#include "main.h"
|
||||
#include "hash.h"
|
||||
#include "network-coding.h"
|
||||
#include "send.h"
|
||||
#include "originator.h"
|
||||
#include "main.h"
|
||||
|
||||
#include <linux/atomic.h>
|
||||
#include <linux/byteorder/generic.h>
|
||||
#include <linux/compiler.h>
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/etherdevice.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/if_ether.h>
|
||||
#include <linux/if_packet.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/jhash.h>
|
||||
#include <linux/jiffies.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/lockdep.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/printk.h>
|
||||
#include <linux/random.h>
|
||||
#include <linux/rculist.h>
|
||||
#include <linux/rcupdate.h>
|
||||
#include <linux/seq_file.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/stat.h>
|
||||
#include <linux/stddef.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/workqueue.h>
|
||||
|
||||
#include "hard-interface.h"
|
||||
#include "hash.h"
|
||||
#include "originator.h"
|
||||
#include "packet.h"
|
||||
#include "routing.h"
|
||||
#include "send.h"
|
||||
|
||||
static struct lock_class_key batadv_nc_coding_hash_lock_class_key;
|
||||
static struct lock_class_key batadv_nc_decoding_hash_lock_class_key;
|
||||
@@ -155,7 +184,7 @@ int batadv_nc_mesh_init(struct batadv_priv *bat_priv)
|
||||
*/
|
||||
void batadv_nc_init_bat_priv(struct batadv_priv *bat_priv)
|
||||
{
|
||||
atomic_set(&bat_priv->network_coding, 1);
|
||||
atomic_set(&bat_priv->network_coding, 0);
|
||||
bat_priv->nc.min_tq = 200;
|
||||
bat_priv->nc.max_fwd_delay = 10;
|
||||
bat_priv->nc.max_buffer_time = 200;
|
||||
@@ -275,7 +304,7 @@ static bool batadv_nc_to_purge_nc_path_decoding(struct batadv_priv *bat_priv,
|
||||
* max_buffer time
|
||||
*/
|
||||
return batadv_has_timed_out(nc_path->last_valid,
|
||||
bat_priv->nc.max_buffer_time*10);
|
||||
bat_priv->nc.max_buffer_time * 10);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -453,14 +482,8 @@ static uint32_t batadv_nc_hash_choose(const void *data, uint32_t size)
|
||||
const struct batadv_nc_path *nc_path = data;
|
||||
uint32_t hash = 0;
|
||||
|
||||
hash = batadv_hash_bytes(hash, &nc_path->prev_hop,
|
||||
sizeof(nc_path->prev_hop));
|
||||
hash = batadv_hash_bytes(hash, &nc_path->next_hop,
|
||||
sizeof(nc_path->next_hop));
|
||||
|
||||
hash += (hash << 3);
|
||||
hash ^= (hash >> 11);
|
||||
hash += (hash << 15);
|
||||
hash = jhash(&nc_path->prev_hop, sizeof(nc_path->prev_hop), hash);
|
||||
hash = jhash(&nc_path->next_hop, sizeof(nc_path->next_hop), hash);
|
||||
|
||||
return hash % size;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2012-2014 B.A.T.M.A.N. contributors:
|
||||
/* Copyright (C) 2012-2015 B.A.T.M.A.N. contributors:
|
||||
*
|
||||
* Martin Hundebøll, Jeppe Ledet-Pedersen
|
||||
*
|
||||
@@ -18,6 +18,19 @@
|
||||
#ifndef _NET_BATMAN_ADV_NETWORK_CODING_H_
|
||||
#define _NET_BATMAN_ADV_NETWORK_CODING_H_
|
||||
|
||||
#include "main.h"
|
||||
|
||||
#include <linux/types.h>
|
||||
|
||||
struct batadv_nc_node;
|
||||
struct batadv_neigh_node;
|
||||
struct batadv_ogm_packet;
|
||||
struct batadv_orig_node;
|
||||
struct batadv_priv;
|
||||
struct net_device;
|
||||
struct seq_file;
|
||||
struct sk_buff;
|
||||
|
||||
#ifdef CONFIG_BATMAN_ADV_NC
|
||||
|
||||
void batadv_nc_status_update(struct net_device *net_dev);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2009-2014 B.A.T.M.A.N. contributors:
|
||||
/* Copyright (C) 2009-2015 B.A.T.M.A.N. contributors:
|
||||
*
|
||||
* Marek Lindner, Simon Wunderlich
|
||||
*
|
||||
@@ -15,19 +15,31 @@
|
||||
* along with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "main.h"
|
||||
#include "distributed-arp-table.h"
|
||||
#include "originator.h"
|
||||
#include "hash.h"
|
||||
#include "translation-table.h"
|
||||
#include "routing.h"
|
||||
#include "main.h"
|
||||
|
||||
#include <linux/errno.h>
|
||||
#include <linux/etherdevice.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/jiffies.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/lockdep.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/seq_file.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/workqueue.h>
|
||||
|
||||
#include "distributed-arp-table.h"
|
||||
#include "fragmentation.h"
|
||||
#include "gateway_client.h"
|
||||
#include "hard-interface.h"
|
||||
#include "soft-interface.h"
|
||||
#include "bridge_loop_avoidance.h"
|
||||
#include "network-coding.h"
|
||||
#include "fragmentation.h"
|
||||
#include "hash.h"
|
||||
#include "multicast.h"
|
||||
#include "network-coding.h"
|
||||
#include "routing.h"
|
||||
#include "translation-table.h"
|
||||
|
||||
/* hash class keys */
|
||||
static struct lock_class_key batadv_orig_hash_lock_class_key;
|
||||
@@ -197,13 +209,19 @@ static void batadv_neigh_node_free_rcu(struct rcu_head *rcu)
|
||||
struct hlist_node *node_tmp;
|
||||
struct batadv_neigh_node *neigh_node;
|
||||
struct batadv_neigh_ifinfo *neigh_ifinfo;
|
||||
struct batadv_algo_ops *bao;
|
||||
|
||||
neigh_node = container_of(rcu, struct batadv_neigh_node, rcu);
|
||||
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_now(neigh_ifinfo);
|
||||
}
|
||||
|
||||
if (bao->bat_neigh_free)
|
||||
bao->bat_neigh_free(neigh_node);
|
||||
|
||||
batadv_hardif_free_ref_now(neigh_node->if_incoming);
|
||||
|
||||
kfree(neigh_node);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2007-2014 B.A.T.M.A.N. contributors:
|
||||
/* Copyright (C) 2007-2015 B.A.T.M.A.N. contributors:
|
||||
*
|
||||
* Marek Lindner, Simon Wunderlich
|
||||
*
|
||||
@@ -18,8 +18,21 @@
|
||||
#ifndef _NET_BATMAN_ADV_ORIGINATOR_H_
|
||||
#define _NET_BATMAN_ADV_ORIGINATOR_H_
|
||||
|
||||
#include "main.h"
|
||||
|
||||
#include <linux/atomic.h>
|
||||
#include <linux/compiler.h>
|
||||
#include <linux/if_ether.h>
|
||||
#include <linux/jhash.h>
|
||||
#include <linux/rculist.h>
|
||||
#include <linux/rcupdate.h>
|
||||
#include <linux/stddef.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
#include "hash.h"
|
||||
|
||||
struct seq_file;
|
||||
|
||||
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);
|
||||
@@ -75,20 +88,9 @@ void batadv_orig_node_vlan_free_ref(struct batadv_orig_node_vlan *orig_vlan);
|
||||
*/
|
||||
static inline uint32_t batadv_choose_orig(const void *data, uint32_t size)
|
||||
{
|
||||
const unsigned char *key = data;
|
||||
uint32_t hash = 0;
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < 6; i++) {
|
||||
hash += key[i];
|
||||
hash += (hash << 10);
|
||||
hash ^= (hash >> 6);
|
||||
}
|
||||
|
||||
hash += (hash << 3);
|
||||
hash ^= (hash >> 11);
|
||||
hash += (hash << 15);
|
||||
|
||||
hash = jhash(data, ETH_ALEN, hash);
|
||||
return hash % size;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2007-2014 B.A.T.M.A.N. contributors:
|
||||
/* Copyright (C) 2007-2015 B.A.T.M.A.N. contributors:
|
||||
*
|
||||
* Marek Lindner, Simon Wunderlich
|
||||
*
|
||||
@@ -18,6 +18,9 @@
|
||||
#ifndef _NET_BATMAN_ADV_PACKET_H_
|
||||
#define _NET_BATMAN_ADV_PACKET_H_
|
||||
|
||||
#include <asm/byteorder.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
/**
|
||||
* enum batadv_packettype - types for batman-adv encapsulated packets
|
||||
* @BATADV_IV_OGM: originator messages for B.A.T.M.A.N. IV
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2007-2014 B.A.T.M.A.N. contributors:
|
||||
/* Copyright (C) 2007-2015 B.A.T.M.A.N. contributors:
|
||||
*
|
||||
* Marek Lindner, Simon Wunderlich
|
||||
*
|
||||
@@ -15,20 +15,36 @@
|
||||
* along with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "main.h"
|
||||
#include "routing.h"
|
||||
#include "send.h"
|
||||
#include "soft-interface.h"
|
||||
#include "hard-interface.h"
|
||||
#include "icmp_socket.h"
|
||||
#include "translation-table.h"
|
||||
#include "originator.h"
|
||||
#include "main.h"
|
||||
|
||||
#include <linux/atomic.h>
|
||||
#include <linux/byteorder/generic.h>
|
||||
#include <linux/compiler.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/etherdevice.h>
|
||||
#include <linux/if_ether.h>
|
||||
#include <linux/jiffies.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/printk.h>
|
||||
#include <linux/rculist.h>
|
||||
#include <linux/rcupdate.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/stddef.h>
|
||||
|
||||
#include "bitarray.h"
|
||||
#include "bridge_loop_avoidance.h"
|
||||
#include "distributed-arp-table.h"
|
||||
#include "network-coding.h"
|
||||
#include "fragmentation.h"
|
||||
|
||||
#include <linux/if_vlan.h>
|
||||
#include "hard-interface.h"
|
||||
#include "icmp_socket.h"
|
||||
#include "network-coding.h"
|
||||
#include "originator.h"
|
||||
#include "packet.h"
|
||||
#include "send.h"
|
||||
#include "soft-interface.h"
|
||||
#include "translation-table.h"
|
||||
|
||||
static int batadv_route_unicast_packet(struct sk_buff *skb,
|
||||
struct batadv_hard_iface *recv_if);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2007-2014 B.A.T.M.A.N. contributors:
|
||||
/* Copyright (C) 2007-2015 B.A.T.M.A.N. contributors:
|
||||
*
|
||||
* Marek Lindner, Simon Wunderlich
|
||||
*
|
||||
@@ -18,6 +18,16 @@
|
||||
#ifndef _NET_BATMAN_ADV_ROUTING_H_
|
||||
#define _NET_BATMAN_ADV_ROUTING_H_
|
||||
|
||||
#include "main.h"
|
||||
|
||||
#include <linux/types.h>
|
||||
|
||||
struct batadv_hard_iface;
|
||||
struct batadv_neigh_node;
|
||||
struct batadv_orig_node;
|
||||
struct batadv_priv;
|
||||
struct sk_buff;
|
||||
|
||||
bool batadv_check_management_packet(struct sk_buff *skb,
|
||||
struct batadv_hard_iface *hard_iface,
|
||||
int header_len);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2007-2014 B.A.T.M.A.N. contributors:
|
||||
/* Copyright (C) 2007-2015 B.A.T.M.A.N. contributors:
|
||||
*
|
||||
* Marek Lindner, Simon Wunderlich
|
||||
*
|
||||
@@ -15,19 +15,37 @@
|
||||
* along with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "main.h"
|
||||
#include "distributed-arp-table.h"
|
||||
#include "send.h"
|
||||
#include "routing.h"
|
||||
#include "translation-table.h"
|
||||
#include "soft-interface.h"
|
||||
#include "hard-interface.h"
|
||||
#include "gateway_common.h"
|
||||
#include "gateway_client.h"
|
||||
#include "originator.h"
|
||||
#include "network-coding.h"
|
||||
#include "main.h"
|
||||
|
||||
#include <linux/atomic.h>
|
||||
#include <linux/byteorder/generic.h>
|
||||
#include <linux/etherdevice.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/if_ether.h>
|
||||
#include <linux/if.h>
|
||||
#include <linux/jiffies.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/printk.h>
|
||||
#include <linux/rculist.h>
|
||||
#include <linux/rcupdate.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/stddef.h>
|
||||
#include <linux/workqueue.h>
|
||||
|
||||
#include "distributed-arp-table.h"
|
||||
#include "fragmentation.h"
|
||||
#include "multicast.h"
|
||||
#include "gateway_client.h"
|
||||
#include "hard-interface.h"
|
||||
#include "network-coding.h"
|
||||
#include "originator.h"
|
||||
#include "routing.h"
|
||||
#include "soft-interface.h"
|
||||
#include "translation-table.h"
|
||||
|
||||
static void batadv_send_outstanding_bcast_packet(struct work_struct *work);
|
||||
|
||||
@@ -255,8 +273,8 @@ int batadv_send_skb_unicast(struct batadv_priv *bat_priv,
|
||||
struct batadv_orig_node *orig_node,
|
||||
unsigned short vid)
|
||||
{
|
||||
struct ethhdr *ethhdr;
|
||||
struct batadv_unicast_packet *unicast_packet;
|
||||
struct ethhdr *ethhdr;
|
||||
int ret = NET_XMIT_DROP;
|
||||
|
||||
if (!orig_node)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2007-2014 B.A.T.M.A.N. contributors:
|
||||
/* Copyright (C) 2007-2015 B.A.T.M.A.N. contributors:
|
||||
*
|
||||
* Marek Lindner, Simon Wunderlich
|
||||
*
|
||||
@@ -18,6 +18,19 @@
|
||||
#ifndef _NET_BATMAN_ADV_SEND_H_
|
||||
#define _NET_BATMAN_ADV_SEND_H_
|
||||
|
||||
#include "main.h"
|
||||
|
||||
#include <linux/compiler.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
#include "packet.h"
|
||||
|
||||
struct batadv_hard_iface;
|
||||
struct batadv_orig_node;
|
||||
struct batadv_priv;
|
||||
struct sk_buff;
|
||||
struct work_struct;
|
||||
|
||||
int batadv_send_skb_packet(struct sk_buff *skb,
|
||||
struct batadv_hard_iface *hard_iface,
|
||||
const uint8_t *dst_addr);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2007-2014 B.A.T.M.A.N. contributors:
|
||||
/* Copyright (C) 2007-2015 B.A.T.M.A.N. contributors:
|
||||
*
|
||||
* Marek Lindner, Simon Wunderlich
|
||||
*
|
||||
@@ -15,26 +15,50 @@
|
||||
* along with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "main.h"
|
||||
#include "soft-interface.h"
|
||||
#include "hard-interface.h"
|
||||
#include "distributed-arp-table.h"
|
||||
#include "routing.h"
|
||||
#include "send.h"
|
||||
#include "debugfs.h"
|
||||
#include "translation-table.h"
|
||||
#include "hash.h"
|
||||
#include "gateway_common.h"
|
||||
#include "gateway_client.h"
|
||||
#include "sysfs.h"
|
||||
#include "originator.h"
|
||||
#include <linux/slab.h>
|
||||
#include <linux/ethtool.h>
|
||||
#include "main.h"
|
||||
|
||||
#include <linux/atomic.h>
|
||||
#include <linux/byteorder/generic.h>
|
||||
#include <linux/cache.h>
|
||||
#include <linux/compiler.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/etherdevice.h>
|
||||
#include <linux/ethtool.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/if_ether.h>
|
||||
#include <linux/if_vlan.h>
|
||||
#include "multicast.h"
|
||||
#include <linux/jiffies.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/lockdep.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/percpu.h>
|
||||
#include <linux/printk.h>
|
||||
#include <linux/random.h>
|
||||
#include <linux/rculist.h>
|
||||
#include <linux/rcupdate.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/socket.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/stddef.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/workqueue.h>
|
||||
|
||||
#include "bridge_loop_avoidance.h"
|
||||
#include "debugfs.h"
|
||||
#include "distributed-arp-table.h"
|
||||
#include "gateway_client.h"
|
||||
#include "gateway_common.h"
|
||||
#include "hard-interface.h"
|
||||
#include "multicast.h"
|
||||
#include "network-coding.h"
|
||||
#include "packet.h"
|
||||
#include "send.h"
|
||||
#include "sysfs.h"
|
||||
#include "translation-table.h"
|
||||
|
||||
static int batadv_get_settings(struct net_device *dev, struct ethtool_cmd *cmd);
|
||||
static void batadv_get_drvinfo(struct net_device *dev,
|
||||
@@ -105,6 +129,7 @@ static struct net_device_stats *batadv_interface_stats(struct net_device *dev)
|
||||
static int batadv_interface_set_mac_addr(struct net_device *dev, void *p)
|
||||
{
|
||||
struct batadv_priv *bat_priv = netdev_priv(dev);
|
||||
struct batadv_softif_vlan *vlan;
|
||||
struct sockaddr *addr = p;
|
||||
uint8_t old_addr[ETH_ALEN];
|
||||
|
||||
@@ -115,12 +140,17 @@ static int batadv_interface_set_mac_addr(struct net_device *dev, void *p)
|
||||
ether_addr_copy(dev->dev_addr, addr->sa_data);
|
||||
|
||||
/* only modify transtable if it has been initialized before */
|
||||
if (atomic_read(&bat_priv->mesh_state) == BATADV_MESH_ACTIVE) {
|
||||
batadv_tt_local_remove(bat_priv, old_addr, BATADV_NO_FLAGS,
|
||||
if (atomic_read(&bat_priv->mesh_state) != BATADV_MESH_ACTIVE)
|
||||
return 0;
|
||||
|
||||
rcu_read_lock();
|
||||
hlist_for_each_entry_rcu(vlan, &bat_priv->softif_vlan_list, list) {
|
||||
batadv_tt_local_remove(bat_priv, old_addr, vlan->vid,
|
||||
"mac address changed", false);
|
||||
batadv_tt_local_add(dev, addr->sa_data, BATADV_NO_FLAGS,
|
||||
batadv_tt_local_add(dev, addr->sa_data, vlan->vid,
|
||||
BATADV_NULL_IFINDEX, BATADV_NO_MARK);
|
||||
}
|
||||
rcu_read_unlock();
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -732,7 +762,7 @@ static int batadv_softif_init_late(struct net_device *dev)
|
||||
atomic_set(&bat_priv->aggregated_ogms, 1);
|
||||
atomic_set(&bat_priv->bonding, 0);
|
||||
#ifdef CONFIG_BATMAN_ADV_BLA
|
||||
atomic_set(&bat_priv->bridge_loop_avoidance, 0);
|
||||
atomic_set(&bat_priv->bridge_loop_avoidance, 1);
|
||||
#endif
|
||||
#ifdef CONFIG_BATMAN_ADV_DAT
|
||||
atomic_set(&bat_priv->distributed_arp_table, 1);
|
||||
@@ -818,7 +848,7 @@ static int batadv_softif_slave_add(struct net_device *dev,
|
||||
int ret = -EINVAL;
|
||||
|
||||
hard_iface = batadv_hardif_get_by_netdev(slave_dev);
|
||||
if (!hard_iface || hard_iface->soft_iface != NULL)
|
||||
if (!hard_iface || hard_iface->soft_iface)
|
||||
goto out;
|
||||
|
||||
ret = batadv_hardif_enable_interface(hard_iface, dev->name);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2007-2014 B.A.T.M.A.N. contributors:
|
||||
/* Copyright (C) 2007-2015 B.A.T.M.A.N. contributors:
|
||||
*
|
||||
* Marek Lindner
|
||||
*
|
||||
@@ -18,6 +18,17 @@
|
||||
#ifndef _NET_BATMAN_ADV_SOFT_INTERFACE_H_
|
||||
#define _NET_BATMAN_ADV_SOFT_INTERFACE_H_
|
||||
|
||||
#include "main.h"
|
||||
|
||||
#include <net/rtnetlink.h>
|
||||
|
||||
struct batadv_hard_iface;
|
||||
struct batadv_orig_node;
|
||||
struct batadv_priv;
|
||||
struct batadv_softif_vlan;
|
||||
struct net_device;
|
||||
struct sk_buff;
|
||||
|
||||
int batadv_skb_head_push(struct sk_buff *skb, unsigned int len);
|
||||
void batadv_interface_rx(struct net_device *soft_iface,
|
||||
struct sk_buff *skb, struct batadv_hard_iface *recv_if,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2010-2014 B.A.T.M.A.N. contributors:
|
||||
/* Copyright (C) 2010-2015 B.A.T.M.A.N. contributors:
|
||||
*
|
||||
* Marek Lindner
|
||||
*
|
||||
@@ -15,16 +15,35 @@
|
||||
* along with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "main.h"
|
||||
#include "sysfs.h"
|
||||
#include "translation-table.h"
|
||||
#include "main.h"
|
||||
|
||||
#include <linux/atomic.h>
|
||||
#include <linux/compiler.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/if.h>
|
||||
#include <linux/if_vlan.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/printk.h>
|
||||
#include <linux/rculist.h>
|
||||
#include <linux/rcupdate.h>
|
||||
#include <linux/rtnetlink.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/stat.h>
|
||||
#include <linux/stddef.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/stringify.h>
|
||||
|
||||
#include "distributed-arp-table.h"
|
||||
#include "network-coding.h"
|
||||
#include "originator.h"
|
||||
#include "hard-interface.h"
|
||||
#include "soft-interface.h"
|
||||
#include "gateway_common.h"
|
||||
#include "gateway_client.h"
|
||||
#include "gateway_common.h"
|
||||
#include "hard-interface.h"
|
||||
#include "network-coding.h"
|
||||
#include "packet.h"
|
||||
#include "soft-interface.h"
|
||||
|
||||
static struct net_device *batadv_kobj_to_netdev(struct kobject *obj)
|
||||
{
|
||||
@@ -151,7 +170,7 @@ ssize_t batadv_show_##_name(struct kobject *kobj, \
|
||||
static BATADV_ATTR(_name, _mode, batadv_show_##_name, \
|
||||
batadv_store_##_name)
|
||||
|
||||
#define BATADV_ATTR_SIF_STORE_UINT(_name, _min, _max, _post_func) \
|
||||
#define BATADV_ATTR_SIF_STORE_UINT(_name, _var, _min, _max, _post_func) \
|
||||
ssize_t batadv_store_##_name(struct kobject *kobj, \
|
||||
struct attribute *attr, char *buff, \
|
||||
size_t count) \
|
||||
@@ -161,24 +180,24 @@ ssize_t batadv_store_##_name(struct kobject *kobj, \
|
||||
\
|
||||
return __batadv_store_uint_attr(buff, count, _min, _max, \
|
||||
_post_func, attr, \
|
||||
&bat_priv->_name, net_dev); \
|
||||
&bat_priv->_var, net_dev); \
|
||||
}
|
||||
|
||||
#define BATADV_ATTR_SIF_SHOW_UINT(_name) \
|
||||
#define BATADV_ATTR_SIF_SHOW_UINT(_name, _var) \
|
||||
ssize_t batadv_show_##_name(struct kobject *kobj, \
|
||||
struct attribute *attr, char *buff) \
|
||||
{ \
|
||||
struct batadv_priv *bat_priv = batadv_kobj_to_batpriv(kobj); \
|
||||
\
|
||||
return sprintf(buff, "%i\n", atomic_read(&bat_priv->_name)); \
|
||||
return sprintf(buff, "%i\n", atomic_read(&bat_priv->_var)); \
|
||||
} \
|
||||
|
||||
/* Use this, if you are going to set [name] in the soft-interface
|
||||
* (bat_priv) to an unsigned integer value
|
||||
*/
|
||||
#define BATADV_ATTR_SIF_UINT(_name, _mode, _min, _max, _post_func) \
|
||||
static BATADV_ATTR_SIF_STORE_UINT(_name, _min, _max, _post_func)\
|
||||
static BATADV_ATTR_SIF_SHOW_UINT(_name) \
|
||||
#define BATADV_ATTR_SIF_UINT(_name, _var, _mode, _min, _max, _post_func)\
|
||||
static BATADV_ATTR_SIF_STORE_UINT(_name, _var, _min, _max, _post_func)\
|
||||
static BATADV_ATTR_SIF_SHOW_UINT(_name, _var) \
|
||||
static BATADV_ATTR(_name, _mode, batadv_show_##_name, \
|
||||
batadv_store_##_name)
|
||||
|
||||
@@ -540,19 +559,20 @@ BATADV_ATTR_SIF_BOOL(fragmentation, S_IRUGO | S_IWUSR, batadv_update_min_mtu);
|
||||
static BATADV_ATTR(routing_algo, S_IRUGO, batadv_show_bat_algo, NULL);
|
||||
static BATADV_ATTR(gw_mode, S_IRUGO | S_IWUSR, batadv_show_gw_mode,
|
||||
batadv_store_gw_mode);
|
||||
BATADV_ATTR_SIF_UINT(orig_interval, S_IRUGO | S_IWUSR, 2 * BATADV_JITTER,
|
||||
INT_MAX, NULL);
|
||||
BATADV_ATTR_SIF_UINT(hop_penalty, S_IRUGO | S_IWUSR, 0, BATADV_TQ_MAX_VALUE,
|
||||
NULL);
|
||||
BATADV_ATTR_SIF_UINT(gw_sel_class, S_IRUGO | S_IWUSR, 1, BATADV_TQ_MAX_VALUE,
|
||||
batadv_post_gw_reselect);
|
||||
BATADV_ATTR_SIF_UINT(orig_interval, orig_interval, S_IRUGO | S_IWUSR,
|
||||
2 * BATADV_JITTER, INT_MAX, NULL);
|
||||
BATADV_ATTR_SIF_UINT(hop_penalty, hop_penalty, S_IRUGO | S_IWUSR, 0,
|
||||
BATADV_TQ_MAX_VALUE, NULL);
|
||||
BATADV_ATTR_SIF_UINT(gw_sel_class, gw_sel_class, S_IRUGO | S_IWUSR, 1,
|
||||
BATADV_TQ_MAX_VALUE, batadv_post_gw_reselect);
|
||||
static BATADV_ATTR(gw_bandwidth, S_IRUGO | S_IWUSR, batadv_show_gw_bwidth,
|
||||
batadv_store_gw_bwidth);
|
||||
#ifdef CONFIG_BATMAN_ADV_MCAST
|
||||
BATADV_ATTR_SIF_BOOL(multicast_mode, S_IRUGO | S_IWUSR, NULL);
|
||||
#endif
|
||||
#ifdef CONFIG_BATMAN_ADV_DEBUG
|
||||
BATADV_ATTR_SIF_UINT(log_level, S_IRUGO | S_IWUSR, 0, BATADV_DBG_ALL, NULL);
|
||||
BATADV_ATTR_SIF_UINT(log_level, log_level, S_IRUGO | S_IWUSR, 0,
|
||||
BATADV_DBG_ALL, NULL);
|
||||
#endif
|
||||
#ifdef CONFIG_BATMAN_ADV_NC
|
||||
BATADV_ATTR_SIF_BOOL(network_coding, S_IRUGO | S_IWUSR,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2010-2014 B.A.T.M.A.N. contributors:
|
||||
/* Copyright (C) 2010-2015 B.A.T.M.A.N. contributors:
|
||||
*
|
||||
* Marek Lindner
|
||||
*
|
||||
@@ -18,6 +18,16 @@
|
||||
#ifndef _NET_BATMAN_ADV_SYSFS_H_
|
||||
#define _NET_BATMAN_ADV_SYSFS_H_
|
||||
|
||||
#include "main.h"
|
||||
|
||||
#include <linux/sysfs.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
struct batadv_priv;
|
||||
struct batadv_softif_vlan;
|
||||
struct kobject;
|
||||
struct net_device;
|
||||
|
||||
#define BATADV_SYSFS_IF_MESH_SUBDIR "mesh"
|
||||
#define BATADV_SYSFS_IF_BAT_SUBDIR "batman_adv"
|
||||
/**
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2007-2014 B.A.T.M.A.N. contributors:
|
||||
/* Copyright (C) 2007-2015 B.A.T.M.A.N. contributors:
|
||||
*
|
||||
* Marek Lindner, Simon Wunderlich, Antonio Quartulli
|
||||
*
|
||||
@@ -15,18 +15,41 @@
|
||||
* along with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "main.h"
|
||||
#include "translation-table.h"
|
||||
#include "soft-interface.h"
|
||||
#include "hard-interface.h"
|
||||
#include "send.h"
|
||||
#include "hash.h"
|
||||
#include "originator.h"
|
||||
#include "routing.h"
|
||||
#include "bridge_loop_avoidance.h"
|
||||
#include "multicast.h"
|
||||
#include "main.h"
|
||||
|
||||
#include <linux/atomic.h>
|
||||
#include <linux/bug.h>
|
||||
#include <linux/byteorder/generic.h>
|
||||
#include <linux/compiler.h>
|
||||
#include <linux/crc32c.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/etherdevice.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/if_ether.h>
|
||||
#include <linux/jhash.h>
|
||||
#include <linux/jiffies.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/lockdep.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/rculist.h>
|
||||
#include <linux/rcupdate.h>
|
||||
#include <linux/seq_file.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/stddef.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/workqueue.h>
|
||||
#include <net/net_namespace.h>
|
||||
|
||||
#include "bridge_loop_avoidance.h"
|
||||
#include "hard-interface.h"
|
||||
#include "hash.h"
|
||||
#include "multicast.h"
|
||||
#include "originator.h"
|
||||
#include "packet.h"
|
||||
#include "soft-interface.h"
|
||||
|
||||
/* hash class keys */
|
||||
static struct lock_class_key batadv_tt_local_hash_lock_class_key;
|
||||
@@ -67,12 +90,8 @@ static inline uint32_t batadv_choose_tt(const void *data, uint32_t size)
|
||||
uint32_t hash = 0;
|
||||
|
||||
tt = (struct batadv_tt_common_entry *)data;
|
||||
hash = batadv_hash_bytes(hash, &tt->addr, ETH_ALEN);
|
||||
hash = batadv_hash_bytes(hash, &tt->vid, sizeof(tt->vid));
|
||||
|
||||
hash += (hash << 3);
|
||||
hash ^= (hash >> 11);
|
||||
hash += (hash << 15);
|
||||
hash = jhash(&tt->addr, ETH_ALEN, hash);
|
||||
hash = jhash(&tt->vid, sizeof(tt->vid), hash);
|
||||
|
||||
return hash % size;
|
||||
}
|
||||
@@ -954,17 +973,17 @@ int batadv_tt_local_seq_print_text(struct seq_file *seq, void *offset)
|
||||
" * %pM %4i [%c%c%c%c%c%c] %3u.%03u (%#.8x)\n",
|
||||
tt_common_entry->addr,
|
||||
BATADV_PRINT_VID(tt_common_entry->vid),
|
||||
(tt_common_entry->flags &
|
||||
BATADV_TT_CLIENT_ROAM ? 'R' : '.'),
|
||||
((tt_common_entry->flags &
|
||||
BATADV_TT_CLIENT_ROAM) ? 'R' : '.'),
|
||||
no_purge ? 'P' : '.',
|
||||
(tt_common_entry->flags &
|
||||
BATADV_TT_CLIENT_NEW ? 'N' : '.'),
|
||||
(tt_common_entry->flags &
|
||||
BATADV_TT_CLIENT_PENDING ? 'X' : '.'),
|
||||
(tt_common_entry->flags &
|
||||
BATADV_TT_CLIENT_WIFI ? 'W' : '.'),
|
||||
(tt_common_entry->flags &
|
||||
BATADV_TT_CLIENT_ISOLA ? 'I' : '.'),
|
||||
((tt_common_entry->flags &
|
||||
BATADV_TT_CLIENT_NEW) ? 'N' : '.'),
|
||||
((tt_common_entry->flags &
|
||||
BATADV_TT_CLIENT_PENDING) ? 'X' : '.'),
|
||||
((tt_common_entry->flags &
|
||||
BATADV_TT_CLIENT_WIFI) ? 'W' : '.'),
|
||||
((tt_common_entry->flags &
|
||||
BATADV_TT_CLIENT_ISOLA) ? 'I' : '.'),
|
||||
no_purge ? 0 : last_seen_secs,
|
||||
no_purge ? 0 : last_seen_msecs,
|
||||
vlan->tt.crc);
|
||||
@@ -1528,10 +1547,10 @@ batadv_tt_global_print_entry(struct batadv_priv *bat_priv,
|
||||
BATADV_PRINT_VID(tt_global_entry->common.vid),
|
||||
best_entry->ttvn, best_entry->orig_node->orig,
|
||||
last_ttvn, vlan->tt.crc,
|
||||
(flags & BATADV_TT_CLIENT_ROAM ? 'R' : '.'),
|
||||
(flags & BATADV_TT_CLIENT_WIFI ? 'W' : '.'),
|
||||
(flags & BATADV_TT_CLIENT_ISOLA ? 'I' : '.'),
|
||||
(flags & BATADV_TT_CLIENT_TEMP ? 'T' : '.'));
|
||||
((flags & BATADV_TT_CLIENT_ROAM) ? 'R' : '.'),
|
||||
((flags & BATADV_TT_CLIENT_WIFI) ? 'W' : '.'),
|
||||
((flags & BATADV_TT_CLIENT_ISOLA) ? 'I' : '.'),
|
||||
((flags & BATADV_TT_CLIENT_TEMP) ? 'T' : '.'));
|
||||
|
||||
batadv_orig_node_vlan_free_ref(vlan);
|
||||
}
|
||||
@@ -1560,10 +1579,10 @@ batadv_tt_global_print_entry(struct batadv_priv *bat_priv,
|
||||
BATADV_PRINT_VID(tt_global_entry->common.vid),
|
||||
orig_entry->ttvn, orig_entry->orig_node->orig,
|
||||
last_ttvn, vlan->tt.crc,
|
||||
(flags & BATADV_TT_CLIENT_ROAM ? 'R' : '.'),
|
||||
(flags & BATADV_TT_CLIENT_WIFI ? 'W' : '.'),
|
||||
(flags & BATADV_TT_CLIENT_ISOLA ? 'I' : '.'),
|
||||
(flags & BATADV_TT_CLIENT_TEMP ? 'T' : '.'));
|
||||
((flags & BATADV_TT_CLIENT_ROAM) ? 'R' : '.'),
|
||||
((flags & BATADV_TT_CLIENT_WIFI) ? 'W' : '.'),
|
||||
((flags & BATADV_TT_CLIENT_ISOLA) ? 'I' : '.'),
|
||||
((flags & BATADV_TT_CLIENT_TEMP) ? 'T' : '.'));
|
||||
|
||||
batadv_orig_node_vlan_free_ref(vlan);
|
||||
}
|
||||
@@ -2529,7 +2548,7 @@ static bool batadv_send_other_tt_response(struct batadv_priv *bat_priv,
|
||||
batadv_dbg(BATADV_DBG_TT, bat_priv,
|
||||
"Received TT_REQUEST from %pM for ttvn: %u (%pM) [%c]\n",
|
||||
req_src, tt_data->ttvn, req_dst,
|
||||
(tt_data->flags & BATADV_TT_FULL_TABLE ? 'F' : '.'));
|
||||
((tt_data->flags & BATADV_TT_FULL_TABLE) ? 'F' : '.'));
|
||||
|
||||
/* Let's get the orig node of the REAL destination */
|
||||
req_dst_orig_node = batadv_orig_hash_find(bat_priv, req_dst);
|
||||
@@ -2660,7 +2679,7 @@ static bool batadv_send_my_tt_response(struct batadv_priv *bat_priv,
|
||||
batadv_dbg(BATADV_DBG_TT, bat_priv,
|
||||
"Received TT_REQUEST from %pM for ttvn: %u (me) [%c]\n",
|
||||
req_src, tt_data->ttvn,
|
||||
(tt_data->flags & BATADV_TT_FULL_TABLE ? 'F' : '.'));
|
||||
((tt_data->flags & BATADV_TT_FULL_TABLE) ? 'F' : '.'));
|
||||
|
||||
spin_lock_bh(&bat_priv->tt.commit_lock);
|
||||
|
||||
@@ -2899,7 +2918,7 @@ static void batadv_handle_tt_response(struct batadv_priv *bat_priv,
|
||||
batadv_dbg(BATADV_DBG_TT, bat_priv,
|
||||
"Received TT_RESPONSE from %pM for ttvn %d t_size: %d [%c]\n",
|
||||
resp_src, tt_data->ttvn, num_entries,
|
||||
(tt_data->flags & BATADV_TT_FULL_TABLE ? 'F' : '.'));
|
||||
((tt_data->flags & BATADV_TT_FULL_TABLE) ? 'F' : '.'));
|
||||
|
||||
orig_node = batadv_orig_hash_find(bat_priv, resp_src);
|
||||
if (!orig_node)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2007-2014 B.A.T.M.A.N. contributors:
|
||||
/* Copyright (C) 2007-2015 B.A.T.M.A.N. contributors:
|
||||
*
|
||||
* Marek Lindner, Simon Wunderlich, Antonio Quartulli
|
||||
*
|
||||
@@ -18,6 +18,15 @@
|
||||
#ifndef _NET_BATMAN_ADV_TRANSLATION_TABLE_H_
|
||||
#define _NET_BATMAN_ADV_TRANSLATION_TABLE_H_
|
||||
|
||||
#include "main.h"
|
||||
|
||||
#include <linux/types.h>
|
||||
|
||||
struct batadv_orig_node;
|
||||
struct batadv_priv;
|
||||
struct net_device;
|
||||
struct seq_file;
|
||||
|
||||
int batadv_tt_init(struct batadv_priv *bat_priv);
|
||||
bool batadv_tt_local_add(struct net_device *soft_iface, const uint8_t *addr,
|
||||
unsigned short vid, int ifindex, uint32_t mark);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2007-2014 B.A.T.M.A.N. contributors:
|
||||
/* Copyright (C) 2007-2015 B.A.T.M.A.N. contributors:
|
||||
*
|
||||
* Marek Lindner, Simon Wunderlich
|
||||
*
|
||||
@@ -18,9 +18,23 @@
|
||||
#ifndef _NET_BATMAN_ADV_TYPES_H_
|
||||
#define _NET_BATMAN_ADV_TYPES_H_
|
||||
|
||||
#ifndef _NET_BATMAN_ADV_MAIN_H_
|
||||
#error only "main.h" can be included directly
|
||||
#endif
|
||||
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/compiler.h>
|
||||
#include <linux/if_ether.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/sched.h> /* for linux/wait.h */
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/wait.h>
|
||||
#include <linux/workqueue.h>
|
||||
|
||||
#include "packet.h"
|
||||
#include "bitarray.h"
|
||||
#include <linux/kernel.h>
|
||||
|
||||
struct seq_file;
|
||||
|
||||
#ifdef CONFIG_BATMAN_ADV_DAT
|
||||
|
||||
@@ -132,6 +146,7 @@ struct batadv_orig_ifinfo {
|
||||
* @timestamp: time (jiffie) of last received fragment
|
||||
* @seqno: sequence number of the fragments in the list
|
||||
* @size: accumulated size of packets in list
|
||||
* @total_size: expected size of the assembled packet
|
||||
*/
|
||||
struct batadv_frag_table_entry {
|
||||
struct hlist_head head;
|
||||
@@ -139,6 +154,7 @@ struct batadv_frag_table_entry {
|
||||
unsigned long timestamp;
|
||||
uint16_t seqno;
|
||||
uint16_t size;
|
||||
uint16_t total_size;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -181,9 +197,10 @@ struct batadv_orig_node_vlan {
|
||||
|
||||
/**
|
||||
* struct batadv_orig_bat_iv - B.A.T.M.A.N. IV private orig_node members
|
||||
* @bcast_own: bitfield containing the number of our OGMs this orig_node
|
||||
* rebroadcasted "back" to us (relative to last_real_seqno)
|
||||
* @bcast_own_sum: counted result of bcast_own
|
||||
* @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
|
||||
* @ogm_cnt_lock: lock protecting bcast_own, bcast_own_sum,
|
||||
* neigh_node->bat_iv.real_bits & neigh_node->bat_iv.real_packet_count
|
||||
*/
|
||||
@@ -1118,6 +1135,8 @@ struct batadv_forw_packet {
|
||||
* @bat_neigh_is_equiv_or_better: check if neigh1 is equally good or better
|
||||
* than neigh2 for their respective outgoing interface from the metric
|
||||
* prospective
|
||||
* @bat_neigh_free: free the resources allocated by the routing algorithm for a
|
||||
* neigh_node object
|
||||
* @bat_orig_print: print the originator table (optional)
|
||||
* @bat_orig_free: free the resources allocated by the routing algorithm for an
|
||||
* orig_node object
|
||||
@@ -1135,6 +1154,7 @@ struct batadv_algo_ops {
|
||||
void (*bat_primary_iface_set)(struct batadv_hard_iface *hard_iface);
|
||||
void (*bat_ogm_schedule)(struct batadv_hard_iface *hard_iface);
|
||||
void (*bat_ogm_emit)(struct batadv_forw_packet *forw_packet);
|
||||
/* neigh_node handling API */
|
||||
int (*bat_neigh_cmp)(struct batadv_neigh_node *neigh1,
|
||||
struct batadv_hard_iface *if_outgoing1,
|
||||
struct batadv_neigh_node *neigh2,
|
||||
@@ -1144,6 +1164,7 @@ struct batadv_algo_ops {
|
||||
struct batadv_hard_iface *if_outgoing1,
|
||||
struct batadv_neigh_node *neigh2,
|
||||
struct batadv_hard_iface *if_outgoing2);
|
||||
void (*bat_neigh_free)(struct batadv_neigh_node *neigh);
|
||||
/* orig_node handling API */
|
||||
void (*bat_orig_print)(struct batadv_priv *priv, struct seq_file *seq,
|
||||
struct batadv_hard_iface *hard_iface);
|
||||
|
||||
@@ -192,7 +192,7 @@ static inline struct lowpan_peer *peer_lookup_dst(struct lowpan_dev *dev,
|
||||
if (ipv6_addr_any(nexthop))
|
||||
return NULL;
|
||||
} else {
|
||||
nexthop = rt6_nexthop(rt);
|
||||
nexthop = rt6_nexthop(rt, daddr);
|
||||
|
||||
/* We need to remember the address because it is needed
|
||||
* by bt_xmit() when sending the packet. In bt_xmit(), the
|
||||
@@ -856,7 +856,7 @@ static int setup_netdev(struct l2cap_chan *chan, struct lowpan_dev **dev)
|
||||
set_dev_addr(netdev, &chan->src, chan->src_type);
|
||||
|
||||
netdev->netdev_ops = &netdev_ops;
|
||||
SET_NETDEV_DEV(netdev, &chan->conn->hcon->dev);
|
||||
SET_NETDEV_DEV(netdev, &chan->conn->hcon->hdev->dev);
|
||||
SET_NETDEV_DEVTYPE(netdev, &bt_type);
|
||||
|
||||
err = register_netdev(netdev);
|
||||
@@ -928,7 +928,7 @@ static void delete_netdev(struct work_struct *work)
|
||||
|
||||
unregister_netdev(entry->netdev);
|
||||
|
||||
/* The entry pointer is deleted in device_event() */
|
||||
/* The entry pointer is deleted by the netdev destructor. */
|
||||
}
|
||||
|
||||
static void chan_close_cb(struct l2cap_chan *chan)
|
||||
@@ -937,7 +937,7 @@ static void chan_close_cb(struct l2cap_chan *chan)
|
||||
struct lowpan_dev *dev = NULL;
|
||||
struct lowpan_peer *peer;
|
||||
int err = -ENOENT;
|
||||
bool last = false, removed = true;
|
||||
bool last = false, remove = true;
|
||||
|
||||
BT_DBG("chan %p conn %p", chan, chan->conn);
|
||||
|
||||
@@ -948,7 +948,7 @@ static void chan_close_cb(struct l2cap_chan *chan)
|
||||
/* If conn is set, then the netdev is also there and we should
|
||||
* not remove it.
|
||||
*/
|
||||
removed = false;
|
||||
remove = false;
|
||||
}
|
||||
|
||||
spin_lock(&devices_lock);
|
||||
@@ -977,7 +977,7 @@ static void chan_close_cb(struct l2cap_chan *chan)
|
||||
|
||||
ifdown(dev->netdev);
|
||||
|
||||
if (!removed) {
|
||||
if (remove) {
|
||||
INIT_WORK(&entry->delete_netdev, delete_netdev);
|
||||
schedule_work(&entry->delete_netdev);
|
||||
}
|
||||
@@ -1208,8 +1208,6 @@ static void disconnect_all_peers(void)
|
||||
|
||||
list_del_rcu(&peer->list);
|
||||
kfree_rcu(peer, rcu);
|
||||
|
||||
module_put(THIS_MODULE);
|
||||
}
|
||||
spin_unlock(&devices_lock);
|
||||
}
|
||||
@@ -1418,7 +1416,6 @@ static int device_event(struct notifier_block *unused,
|
||||
BT_DBG("Unregistered netdev %s %p",
|
||||
netdev->name, netdev);
|
||||
list_del(&entry->list);
|
||||
kfree(entry);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,9 +12,10 @@ obj-$(CONFIG_BT_6LOWPAN) += bluetooth_6lowpan.o
|
||||
bluetooth_6lowpan-y := 6lowpan.o
|
||||
|
||||
bluetooth-y := af_bluetooth.o hci_core.o hci_conn.o hci_event.o mgmt.o \
|
||||
hci_sock.o hci_sysfs.o l2cap_core.o l2cap_sock.o smp.o sco.o lib.o \
|
||||
hci_sock.o hci_sysfs.o l2cap_core.o l2cap_sock.o smp.o lib.o \
|
||||
a2mp.o amp.o ecc.o hci_request.o mgmt_util.o
|
||||
|
||||
bluetooth-$(CONFIG_BT_BREDR) += sco.o
|
||||
bluetooth-$(CONFIG_BT_DEBUGFS) += hci_debugfs.o
|
||||
bluetooth-$(CONFIG_BT_SELFTEST) += selftest.o
|
||||
|
||||
|
||||
@@ -202,7 +202,7 @@ static int bnep_sock_create(struct net *net, struct socket *sock, int protocol,
|
||||
if (sock->type != SOCK_RAW)
|
||||
return -ESOCKTNOSUPPORT;
|
||||
|
||||
sk = sk_alloc(net, PF_BLUETOOTH, GFP_ATOMIC, &bnep_proto);
|
||||
sk = sk_alloc(net, PF_BLUETOOTH, GFP_ATOMIC, &bnep_proto, kern);
|
||||
if (!sk)
|
||||
return -ENOMEM;
|
||||
|
||||
|
||||
@@ -205,7 +205,7 @@ static int cmtp_sock_create(struct net *net, struct socket *sock, int protocol,
|
||||
if (sock->type != SOCK_RAW)
|
||||
return -ESOCKTNOSUPPORT;
|
||||
|
||||
sk = sk_alloc(net, PF_BLUETOOTH, GFP_ATOMIC, &cmtp_proto);
|
||||
sk = sk_alloc(net, PF_BLUETOOTH, GFP_ATOMIC, &cmtp_proto, kern);
|
||||
if (!sk)
|
||||
return -ENOMEM;
|
||||
|
||||
|
||||
@@ -276,7 +276,7 @@ u8 hci_le_conn_update(struct hci_conn *conn, u16 min, u16 max, u16 latency,
|
||||
}
|
||||
|
||||
void hci_le_start_enc(struct hci_conn *conn, __le16 ediv, __le64 rand,
|
||||
__u8 ltk[16])
|
||||
__u8 ltk[16], __u8 key_size)
|
||||
{
|
||||
struct hci_dev *hdev = conn->hdev;
|
||||
struct hci_cp_le_start_enc cp;
|
||||
@@ -288,7 +288,7 @@ void hci_le_start_enc(struct hci_conn *conn, __le16 ediv, __le64 rand,
|
||||
cp.handle = cpu_to_le16(conn->handle);
|
||||
cp.rand = rand;
|
||||
cp.ediv = ediv;
|
||||
memcpy(cp.ltk, ltk, sizeof(cp.ltk));
|
||||
memcpy(cp.ltk, ltk, key_size);
|
||||
|
||||
hci_send_cmd(hdev, HCI_OP_LE_START_ENC, sizeof(cp), &cp);
|
||||
}
|
||||
|
||||
@@ -94,7 +94,6 @@ static ssize_t dut_mode_write(struct file *file, const char __user *user_buf,
|
||||
char buf[32];
|
||||
size_t buf_size = min(count, (sizeof(buf)-1));
|
||||
bool enable;
|
||||
int err;
|
||||
|
||||
if (!test_bit(HCI_UP, &hdev->flags))
|
||||
return -ENETDOWN;
|
||||
@@ -121,12 +120,8 @@ static ssize_t dut_mode_write(struct file *file, const char __user *user_buf,
|
||||
if (IS_ERR(skb))
|
||||
return PTR_ERR(skb);
|
||||
|
||||
err = -bt_to_errno(skb->data[0]);
|
||||
kfree_skb(skb);
|
||||
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
hci_dev_change_flag(hdev, HCI_DUT_MODE);
|
||||
|
||||
return count;
|
||||
@@ -1558,6 +1553,7 @@ static int hci_dev_do_close(struct hci_dev *hdev)
|
||||
BT_DBG("%s %p", hdev->name, hdev);
|
||||
|
||||
if (!hci_dev_test_flag(hdev, HCI_UNREGISTER) &&
|
||||
!hci_dev_test_flag(hdev, HCI_USER_CHANNEL) &&
|
||||
test_bit(HCI_UP, &hdev->flags)) {
|
||||
/* Execute vendor specific shutdown routine */
|
||||
if (hdev->shutdown)
|
||||
@@ -1595,6 +1591,11 @@ static int hci_dev_do_close(struct hci_dev *hdev)
|
||||
if (hci_dev_test_flag(hdev, HCI_MGMT))
|
||||
cancel_delayed_work_sync(&hdev->rpa_expired);
|
||||
|
||||
if (hdev->adv_instance_timeout) {
|
||||
cancel_delayed_work_sync(&hdev->adv_instance_expire);
|
||||
hdev->adv_instance_timeout = 0;
|
||||
}
|
||||
|
||||
/* Avoid potential lockdep warnings from the *_flush() calls by
|
||||
* ensuring the workqueue is empty up front.
|
||||
*/
|
||||
@@ -2151,6 +2152,17 @@ static void hci_discov_off(struct work_struct *work)
|
||||
mgmt_discoverable_timeout(hdev);
|
||||
}
|
||||
|
||||
static void hci_adv_timeout_expire(struct work_struct *work)
|
||||
{
|
||||
struct hci_dev *hdev;
|
||||
|
||||
hdev = container_of(work, struct hci_dev, adv_instance_expire.work);
|
||||
|
||||
BT_DBG("%s", hdev->name);
|
||||
|
||||
mgmt_adv_timeout_expired(hdev);
|
||||
}
|
||||
|
||||
void hci_uuids_clear(struct hci_dev *hdev)
|
||||
{
|
||||
struct bt_uuid *uuid, *tmp;
|
||||
@@ -2614,6 +2626,130 @@ int hci_add_remote_oob_data(struct hci_dev *hdev, bdaddr_t *bdaddr,
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* This function requires the caller holds hdev->lock */
|
||||
struct adv_info *hci_find_adv_instance(struct hci_dev *hdev, u8 instance)
|
||||
{
|
||||
struct adv_info *adv_instance;
|
||||
|
||||
list_for_each_entry(adv_instance, &hdev->adv_instances, list) {
|
||||
if (adv_instance->instance == instance)
|
||||
return adv_instance;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* This function requires the caller holds hdev->lock */
|
||||
struct adv_info *hci_get_next_instance(struct hci_dev *hdev, u8 instance) {
|
||||
struct adv_info *cur_instance;
|
||||
|
||||
cur_instance = hci_find_adv_instance(hdev, instance);
|
||||
if (!cur_instance)
|
||||
return NULL;
|
||||
|
||||
if (cur_instance == list_last_entry(&hdev->adv_instances,
|
||||
struct adv_info, list))
|
||||
return list_first_entry(&hdev->adv_instances,
|
||||
struct adv_info, list);
|
||||
else
|
||||
return list_next_entry(cur_instance, list);
|
||||
}
|
||||
|
||||
/* This function requires the caller holds hdev->lock */
|
||||
int hci_remove_adv_instance(struct hci_dev *hdev, u8 instance)
|
||||
{
|
||||
struct adv_info *adv_instance;
|
||||
|
||||
adv_instance = hci_find_adv_instance(hdev, instance);
|
||||
if (!adv_instance)
|
||||
return -ENOENT;
|
||||
|
||||
BT_DBG("%s removing %dMR", hdev->name, instance);
|
||||
|
||||
if (hdev->cur_adv_instance == instance && hdev->adv_instance_timeout) {
|
||||
cancel_delayed_work(&hdev->adv_instance_expire);
|
||||
hdev->adv_instance_timeout = 0;
|
||||
}
|
||||
|
||||
list_del(&adv_instance->list);
|
||||
kfree(adv_instance);
|
||||
|
||||
hdev->adv_instance_cnt--;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* This function requires the caller holds hdev->lock */
|
||||
void hci_adv_instances_clear(struct hci_dev *hdev)
|
||||
{
|
||||
struct adv_info *adv_instance, *n;
|
||||
|
||||
if (hdev->adv_instance_timeout) {
|
||||
cancel_delayed_work(&hdev->adv_instance_expire);
|
||||
hdev->adv_instance_timeout = 0;
|
||||
}
|
||||
|
||||
list_for_each_entry_safe(adv_instance, n, &hdev->adv_instances, list) {
|
||||
list_del(&adv_instance->list);
|
||||
kfree(adv_instance);
|
||||
}
|
||||
|
||||
hdev->adv_instance_cnt = 0;
|
||||
}
|
||||
|
||||
/* This function requires the caller holds hdev->lock */
|
||||
int hci_add_adv_instance(struct hci_dev *hdev, u8 instance, u32 flags,
|
||||
u16 adv_data_len, u8 *adv_data,
|
||||
u16 scan_rsp_len, u8 *scan_rsp_data,
|
||||
u16 timeout, u16 duration)
|
||||
{
|
||||
struct adv_info *adv_instance;
|
||||
|
||||
adv_instance = hci_find_adv_instance(hdev, instance);
|
||||
if (adv_instance) {
|
||||
memset(adv_instance->adv_data, 0,
|
||||
sizeof(adv_instance->adv_data));
|
||||
memset(adv_instance->scan_rsp_data, 0,
|
||||
sizeof(adv_instance->scan_rsp_data));
|
||||
} else {
|
||||
if (hdev->adv_instance_cnt >= HCI_MAX_ADV_INSTANCES ||
|
||||
instance < 1 || instance > HCI_MAX_ADV_INSTANCES)
|
||||
return -EOVERFLOW;
|
||||
|
||||
adv_instance = kzalloc(sizeof(*adv_instance), GFP_KERNEL);
|
||||
if (!adv_instance)
|
||||
return -ENOMEM;
|
||||
|
||||
adv_instance->pending = true;
|
||||
adv_instance->instance = instance;
|
||||
list_add(&adv_instance->list, &hdev->adv_instances);
|
||||
hdev->adv_instance_cnt++;
|
||||
}
|
||||
|
||||
adv_instance->flags = flags;
|
||||
adv_instance->adv_data_len = adv_data_len;
|
||||
adv_instance->scan_rsp_len = scan_rsp_len;
|
||||
|
||||
if (adv_data_len)
|
||||
memcpy(adv_instance->adv_data, adv_data, adv_data_len);
|
||||
|
||||
if (scan_rsp_len)
|
||||
memcpy(adv_instance->scan_rsp_data,
|
||||
scan_rsp_data, scan_rsp_len);
|
||||
|
||||
adv_instance->timeout = timeout;
|
||||
adv_instance->remaining_time = timeout;
|
||||
|
||||
if (duration == 0)
|
||||
adv_instance->duration = HCI_DEFAULT_ADV_DURATION;
|
||||
else
|
||||
adv_instance->duration = duration;
|
||||
|
||||
BT_DBG("%s for %dMR", hdev->name, instance);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct bdaddr_list *hci_bdaddr_list_lookup(struct list_head *bdaddr_list,
|
||||
bdaddr_t *bdaddr, u8 type)
|
||||
{
|
||||
@@ -3019,6 +3155,9 @@ struct hci_dev *hci_alloc_dev(void)
|
||||
hdev->manufacturer = 0xffff; /* Default to internal use */
|
||||
hdev->inq_tx_power = HCI_TX_POWER_INVALID;
|
||||
hdev->adv_tx_power = HCI_TX_POWER_INVALID;
|
||||
hdev->adv_instance_cnt = 0;
|
||||
hdev->cur_adv_instance = 0x00;
|
||||
hdev->adv_instance_timeout = 0;
|
||||
|
||||
hdev->sniff_max_interval = 800;
|
||||
hdev->sniff_min_interval = 80;
|
||||
@@ -3060,6 +3199,7 @@ struct hci_dev *hci_alloc_dev(void)
|
||||
INIT_LIST_HEAD(&hdev->pend_le_conns);
|
||||
INIT_LIST_HEAD(&hdev->pend_le_reports);
|
||||
INIT_LIST_HEAD(&hdev->conn_hash.list);
|
||||
INIT_LIST_HEAD(&hdev->adv_instances);
|
||||
|
||||
INIT_WORK(&hdev->rx_work, hci_rx_work);
|
||||
INIT_WORK(&hdev->cmd_work, hci_cmd_work);
|
||||
@@ -3071,6 +3211,7 @@ struct hci_dev *hci_alloc_dev(void)
|
||||
INIT_DELAYED_WORK(&hdev->discov_off, hci_discov_off);
|
||||
INIT_DELAYED_WORK(&hdev->le_scan_disable, le_scan_disable_work);
|
||||
INIT_DELAYED_WORK(&hdev->le_scan_restart, le_scan_restart_work);
|
||||
INIT_DELAYED_WORK(&hdev->adv_instance_expire, hci_adv_timeout_expire);
|
||||
|
||||
skb_queue_head_init(&hdev->rx_q);
|
||||
skb_queue_head_init(&hdev->cmd_q);
|
||||
@@ -3082,7 +3223,6 @@ struct hci_dev *hci_alloc_dev(void)
|
||||
|
||||
hci_init_sysfs(hdev);
|
||||
discovery_init(hdev);
|
||||
adv_info_init(hdev);
|
||||
|
||||
return hdev;
|
||||
}
|
||||
@@ -3253,6 +3393,7 @@ void hci_unregister_dev(struct hci_dev *hdev)
|
||||
hci_smp_ltks_clear(hdev);
|
||||
hci_smp_irks_clear(hdev);
|
||||
hci_remote_oob_data_clear(hdev);
|
||||
hci_adv_instances_clear(hdev);
|
||||
hci_bdaddr_list_clear(&hdev->le_white_list);
|
||||
hci_conn_params_clear_all(hdev);
|
||||
hci_discovery_filter_clear(hdev);
|
||||
|
||||
@@ -2603,6 +2603,63 @@ static void hci_remote_name_evt(struct hci_dev *hdev, struct sk_buff *skb)
|
||||
hci_dev_unlock(hdev);
|
||||
}
|
||||
|
||||
static void read_enc_key_size_complete(struct hci_dev *hdev, u8 status,
|
||||
u16 opcode, struct sk_buff *skb)
|
||||
{
|
||||
const struct hci_rp_read_enc_key_size *rp;
|
||||
struct hci_conn *conn;
|
||||
u16 handle;
|
||||
|
||||
BT_DBG("%s status 0x%02x", hdev->name, status);
|
||||
|
||||
if (!skb || skb->len < sizeof(*rp)) {
|
||||
BT_ERR("%s invalid HCI Read Encryption Key Size response",
|
||||
hdev->name);
|
||||
return;
|
||||
}
|
||||
|
||||
rp = (void *)skb->data;
|
||||
handle = le16_to_cpu(rp->handle);
|
||||
|
||||
hci_dev_lock(hdev);
|
||||
|
||||
conn = hci_conn_hash_lookup_handle(hdev, handle);
|
||||
if (!conn)
|
||||
goto unlock;
|
||||
|
||||
/* If we fail to read the encryption key size, assume maximum
|
||||
* (which is the same we do also when this HCI command isn't
|
||||
* supported.
|
||||
*/
|
||||
if (rp->status) {
|
||||
BT_ERR("%s failed to read key size for handle %u", hdev->name,
|
||||
handle);
|
||||
conn->enc_key_size = HCI_LINK_KEY_SIZE;
|
||||
} else {
|
||||
conn->enc_key_size = rp->key_size;
|
||||
}
|
||||
|
||||
if (conn->state == BT_CONFIG) {
|
||||
conn->state = BT_CONNECTED;
|
||||
hci_connect_cfm(conn, 0);
|
||||
hci_conn_drop(conn);
|
||||
} else {
|
||||
u8 encrypt;
|
||||
|
||||
if (!test_bit(HCI_CONN_ENCRYPT, &conn->flags))
|
||||
encrypt = 0x00;
|
||||
else if (test_bit(HCI_CONN_AES_CCM, &conn->flags))
|
||||
encrypt = 0x02;
|
||||
else
|
||||
encrypt = 0x01;
|
||||
|
||||
hci_encrypt_cfm(conn, 0, encrypt);
|
||||
}
|
||||
|
||||
unlock:
|
||||
hci_dev_unlock(hdev);
|
||||
}
|
||||
|
||||
static void hci_encrypt_change_evt(struct hci_dev *hdev, struct sk_buff *skb)
|
||||
{
|
||||
struct hci_ev_encrypt_change *ev = (void *) skb->data;
|
||||
@@ -2650,22 +2707,51 @@ static void hci_encrypt_change_evt(struct hci_dev *hdev, struct sk_buff *skb)
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
/* In Secure Connections Only mode, do not allow any connections
|
||||
* that are not encrypted with AES-CCM using a P-256 authenticated
|
||||
* combination key.
|
||||
*/
|
||||
if (hci_dev_test_flag(hdev, HCI_SC_ONLY) &&
|
||||
(!test_bit(HCI_CONN_AES_CCM, &conn->flags) ||
|
||||
conn->key_type != HCI_LK_AUTH_COMBINATION_P256)) {
|
||||
hci_connect_cfm(conn, HCI_ERROR_AUTH_FAILURE);
|
||||
hci_conn_drop(conn);
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
/* Try reading the encryption key size for encrypted ACL links */
|
||||
if (!ev->status && ev->encrypt && conn->type == ACL_LINK) {
|
||||
struct hci_cp_read_enc_key_size cp;
|
||||
struct hci_request req;
|
||||
|
||||
/* Only send HCI_Read_Encryption_Key_Size if the
|
||||
* controller really supports it. If it doesn't, assume
|
||||
* the default size (16).
|
||||
*/
|
||||
if (!(hdev->commands[20] & 0x10)) {
|
||||
conn->enc_key_size = HCI_LINK_KEY_SIZE;
|
||||
goto notify;
|
||||
}
|
||||
|
||||
hci_req_init(&req, hdev);
|
||||
|
||||
cp.handle = cpu_to_le16(conn->handle);
|
||||
hci_req_add(&req, HCI_OP_READ_ENC_KEY_SIZE, sizeof(cp), &cp);
|
||||
|
||||
if (hci_req_run_skb(&req, read_enc_key_size_complete)) {
|
||||
BT_ERR("Sending HCI Read Encryption Key Size failed");
|
||||
conn->enc_key_size = HCI_LINK_KEY_SIZE;
|
||||
goto notify;
|
||||
}
|
||||
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
notify:
|
||||
if (conn->state == BT_CONFIG) {
|
||||
if (!ev->status)
|
||||
conn->state = BT_CONNECTED;
|
||||
|
||||
/* In Secure Connections Only mode, do not allow any
|
||||
* connections that are not encrypted with AES-CCM
|
||||
* using a P-256 authenticated combination key.
|
||||
*/
|
||||
if (hci_dev_test_flag(hdev, HCI_SC_ONLY) &&
|
||||
(!test_bit(HCI_CONN_AES_CCM, &conn->flags) ||
|
||||
conn->key_type != HCI_LK_AUTH_COMBINATION_P256)) {
|
||||
hci_connect_cfm(conn, HCI_ERROR_AUTH_FAILURE);
|
||||
hci_conn_drop(conn);
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
hci_connect_cfm(conn, ev->status);
|
||||
hci_conn_drop(conn);
|
||||
} else
|
||||
@@ -4955,7 +5041,8 @@ static void hci_le_ltk_request_evt(struct hci_dev *hdev, struct sk_buff *skb)
|
||||
goto not_found;
|
||||
}
|
||||
|
||||
memcpy(cp.ltk, ltk->val, sizeof(ltk->val));
|
||||
memcpy(cp.ltk, ltk->val, ltk->enc_size);
|
||||
memset(cp.ltk + ltk->enc_size, 0, sizeof(cp.ltk) - ltk->enc_size);
|
||||
cp.handle = cpu_to_le16(conn->handle);
|
||||
|
||||
conn->pending_sec_level = smp_ltk_sec_level(ltk);
|
||||
|
||||
@@ -503,9 +503,9 @@ static int hci_sock_release(struct socket *sock)
|
||||
|
||||
if (hdev) {
|
||||
if (hci_pi(sk)->channel == HCI_CHANNEL_USER) {
|
||||
mgmt_index_added(hdev);
|
||||
hci_dev_clear_flag(hdev, HCI_USER_CHANNEL);
|
||||
hci_dev_close(hdev->id);
|
||||
hci_dev_clear_flag(hdev, HCI_USER_CHANNEL);
|
||||
mgmt_index_added(hdev);
|
||||
}
|
||||
|
||||
atomic_dec(&hdev->promisc);
|
||||
@@ -741,10 +741,11 @@ static int hci_sock_bind(struct socket *sock, struct sockaddr *addr,
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (test_bit(HCI_UP, &hdev->flags) ||
|
||||
test_bit(HCI_INIT, &hdev->flags) ||
|
||||
if (test_bit(HCI_INIT, &hdev->flags) ||
|
||||
hci_dev_test_flag(hdev, HCI_SETUP) ||
|
||||
hci_dev_test_flag(hdev, HCI_CONFIG)) {
|
||||
hci_dev_test_flag(hdev, HCI_CONFIG) ||
|
||||
(!hci_dev_test_flag(hdev, HCI_AUTO_OFF) &&
|
||||
test_bit(HCI_UP, &hdev->flags))) {
|
||||
err = -EBUSY;
|
||||
hci_dev_put(hdev);
|
||||
goto done;
|
||||
@@ -760,10 +761,21 @@ static int hci_sock_bind(struct socket *sock, struct sockaddr *addr,
|
||||
|
||||
err = hci_dev_open(hdev->id);
|
||||
if (err) {
|
||||
hci_dev_clear_flag(hdev, HCI_USER_CHANNEL);
|
||||
mgmt_index_added(hdev);
|
||||
hci_dev_put(hdev);
|
||||
goto done;
|
||||
if (err == -EALREADY) {
|
||||
/* In case the transport is already up and
|
||||
* running, clear the error here.
|
||||
*
|
||||
* This can happen when opening an user
|
||||
* channel and HCI_AUTO_OFF grace period
|
||||
* is still active.
|
||||
*/
|
||||
err = 0;
|
||||
} else {
|
||||
hci_dev_clear_flag(hdev, HCI_USER_CHANNEL);
|
||||
mgmt_index_added(hdev);
|
||||
hci_dev_put(hdev);
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
|
||||
atomic_inc(&hdev->promisc);
|
||||
@@ -1377,7 +1389,7 @@ static int hci_sock_create(struct net *net, struct socket *sock, int protocol,
|
||||
|
||||
sock->ops = &hci_sock_ops;
|
||||
|
||||
sk = sk_alloc(net, PF_BLUETOOTH, GFP_ATOMIC, &hci_sk_proto);
|
||||
sk = sk_alloc(net, PF_BLUETOOTH, GFP_ATOMIC, &hci_sk_proto, kern);
|
||||
if (!sk)
|
||||
return -ENOMEM;
|
||||
|
||||
|
||||
@@ -235,7 +235,7 @@ static int hidp_sock_create(struct net *net, struct socket *sock, int protocol,
|
||||
if (sock->type != SOCK_RAW)
|
||||
return -ESOCKTNOSUPPORT;
|
||||
|
||||
sk = sk_alloc(net, PF_BLUETOOTH, GFP_ATOMIC, &hidp_proto);
|
||||
sk = sk_alloc(net, PF_BLUETOOTH, GFP_ATOMIC, &hidp_proto, kern);
|
||||
if (!sk)
|
||||
return -ENOMEM;
|
||||
|
||||
|
||||
@@ -1601,7 +1601,7 @@ int l2cap_register_user(struct l2cap_conn *conn, struct l2cap_user *user)
|
||||
|
||||
hci_dev_lock(hdev);
|
||||
|
||||
if (user->list.next || user->list.prev) {
|
||||
if (!list_empty(&user->list)) {
|
||||
ret = -EINVAL;
|
||||
goto out_unlock;
|
||||
}
|
||||
@@ -1631,12 +1631,10 @@ void l2cap_unregister_user(struct l2cap_conn *conn, struct l2cap_user *user)
|
||||
|
||||
hci_dev_lock(hdev);
|
||||
|
||||
if (!user->list.next || !user->list.prev)
|
||||
if (list_empty(&user->list))
|
||||
goto out_unlock;
|
||||
|
||||
list_del(&user->list);
|
||||
user->list.next = NULL;
|
||||
user->list.prev = NULL;
|
||||
user->remove(conn, user);
|
||||
|
||||
out_unlock:
|
||||
@@ -1651,8 +1649,6 @@ static void l2cap_unregister_all_users(struct l2cap_conn *conn)
|
||||
while (!list_empty(&conn->users)) {
|
||||
user = list_first_entry(&conn->users, struct l2cap_user, list);
|
||||
list_del(&user->list);
|
||||
user->list.next = NULL;
|
||||
user->list.prev = NULL;
|
||||
user->remove(conn, user);
|
||||
}
|
||||
}
|
||||
@@ -7442,7 +7438,7 @@ static void l2cap_security_cfm(struct hci_conn *hcon, u8 status, u8 encrypt)
|
||||
mutex_unlock(&conn->chan_lock);
|
||||
}
|
||||
|
||||
int l2cap_recv_acldata(struct hci_conn *hcon, struct sk_buff *skb, u16 flags)
|
||||
void l2cap_recv_acldata(struct hci_conn *hcon, struct sk_buff *skb, u16 flags)
|
||||
{
|
||||
struct l2cap_conn *conn = hcon->l2cap_data;
|
||||
struct l2cap_hdr *hdr;
|
||||
@@ -7485,7 +7481,7 @@ int l2cap_recv_acldata(struct hci_conn *hcon, struct sk_buff *skb, u16 flags)
|
||||
if (len == skb->len) {
|
||||
/* Complete frame received */
|
||||
l2cap_recv_frame(conn, skb);
|
||||
return 0;
|
||||
return;
|
||||
}
|
||||
|
||||
BT_DBG("Start: total len %d, frag len %d", len, skb->len);
|
||||
@@ -7544,7 +7540,6 @@ int l2cap_recv_acldata(struct hci_conn *hcon, struct sk_buff *skb, u16 flags)
|
||||
|
||||
drop:
|
||||
kfree_skb(skb);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct hci_cb l2cap_cb = {
|
||||
|
||||
@@ -43,7 +43,7 @@ static struct bt_sock_list l2cap_sk_list = {
|
||||
static const struct proto_ops l2cap_sock_ops;
|
||||
static void l2cap_sock_init(struct sock *sk, struct sock *parent);
|
||||
static struct sock *l2cap_sock_alloc(struct net *net, struct socket *sock,
|
||||
int proto, gfp_t prio);
|
||||
int proto, gfp_t prio, int kern);
|
||||
|
||||
bool l2cap_is_socket(struct socket *sock)
|
||||
{
|
||||
@@ -1193,7 +1193,7 @@ static struct l2cap_chan *l2cap_sock_new_connection_cb(struct l2cap_chan *chan)
|
||||
}
|
||||
|
||||
sk = l2cap_sock_alloc(sock_net(parent), NULL, BTPROTO_L2CAP,
|
||||
GFP_ATOMIC);
|
||||
GFP_ATOMIC, 0);
|
||||
if (!sk) {
|
||||
release_sock(parent);
|
||||
return NULL;
|
||||
@@ -1523,12 +1523,12 @@ static struct proto l2cap_proto = {
|
||||
};
|
||||
|
||||
static struct sock *l2cap_sock_alloc(struct net *net, struct socket *sock,
|
||||
int proto, gfp_t prio)
|
||||
int proto, gfp_t prio, int kern)
|
||||
{
|
||||
struct sock *sk;
|
||||
struct l2cap_chan *chan;
|
||||
|
||||
sk = sk_alloc(net, PF_BLUETOOTH, prio, &l2cap_proto);
|
||||
sk = sk_alloc(net, PF_BLUETOOTH, prio, &l2cap_proto, kern);
|
||||
if (!sk)
|
||||
return NULL;
|
||||
|
||||
@@ -1574,7 +1574,7 @@ static int l2cap_sock_create(struct net *net, struct socket *sock, int protocol,
|
||||
|
||||
sock->ops = &l2cap_sock_ops;
|
||||
|
||||
sk = l2cap_sock_alloc(net, sock, protocol, GFP_ATOMIC);
|
||||
sk = l2cap_sock_alloc(net, sock, protocol, GFP_ATOMIC, kern);
|
||||
if (!sk)
|
||||
return -ENOMEM;
|
||||
|
||||
|
||||
@@ -38,7 +38,7 @@
|
||||
#include "mgmt_util.h"
|
||||
|
||||
#define MGMT_VERSION 1
|
||||
#define MGMT_REVISION 9
|
||||
#define MGMT_REVISION 10
|
||||
|
||||
static const u16 mgmt_commands[] = {
|
||||
MGMT_OP_READ_INDEX_LIST,
|
||||
@@ -832,6 +832,20 @@ static struct mgmt_pending_cmd *pending_find_data(u16 opcode,
|
||||
return mgmt_pending_find_data(HCI_CHANNEL_CONTROL, opcode, hdev, data);
|
||||
}
|
||||
|
||||
static u8 get_current_adv_instance(struct hci_dev *hdev)
|
||||
{
|
||||
/* The "Set Advertising" setting supersedes the "Add Advertising"
|
||||
* setting. Here we set the advertising data based on which
|
||||
* setting was set. When neither apply, default to the global settings,
|
||||
* represented by instance "0".
|
||||
*/
|
||||
if (hci_dev_test_flag(hdev, HCI_ADVERTISING_INSTANCE) &&
|
||||
!hci_dev_test_flag(hdev, HCI_ADVERTISING))
|
||||
return hdev->cur_adv_instance;
|
||||
|
||||
return 0x00;
|
||||
}
|
||||
|
||||
static u8 create_default_scan_rsp_data(struct hci_dev *hdev, u8 *ptr)
|
||||
{
|
||||
u8 ad_len = 0;
|
||||
@@ -858,19 +872,25 @@ static u8 create_default_scan_rsp_data(struct hci_dev *hdev, u8 *ptr)
|
||||
return ad_len;
|
||||
}
|
||||
|
||||
static u8 create_instance_scan_rsp_data(struct hci_dev *hdev, u8 *ptr)
|
||||
static u8 create_instance_scan_rsp_data(struct hci_dev *hdev, u8 instance,
|
||||
u8 *ptr)
|
||||
{
|
||||
struct adv_info *adv_instance;
|
||||
|
||||
adv_instance = hci_find_adv_instance(hdev, instance);
|
||||
if (!adv_instance)
|
||||
return 0;
|
||||
|
||||
/* TODO: Set the appropriate entries based on advertising instance flags
|
||||
* here once flags other than 0 are supported.
|
||||
*/
|
||||
memcpy(ptr, hdev->adv_instance.scan_rsp_data,
|
||||
hdev->adv_instance.scan_rsp_len);
|
||||
memcpy(ptr, adv_instance->scan_rsp_data,
|
||||
adv_instance->scan_rsp_len);
|
||||
|
||||
return hdev->adv_instance.scan_rsp_len;
|
||||
return adv_instance->scan_rsp_len;
|
||||
}
|
||||
|
||||
static void update_scan_rsp_data_for_instance(struct hci_request *req,
|
||||
u8 instance)
|
||||
static void update_inst_scan_rsp_data(struct hci_request *req, u8 instance)
|
||||
{
|
||||
struct hci_dev *hdev = req->hdev;
|
||||
struct hci_cp_le_set_scan_rsp_data cp;
|
||||
@@ -882,7 +902,7 @@ static void update_scan_rsp_data_for_instance(struct hci_request *req,
|
||||
memset(&cp, 0, sizeof(cp));
|
||||
|
||||
if (instance)
|
||||
len = create_instance_scan_rsp_data(hdev, cp.data);
|
||||
len = create_instance_scan_rsp_data(hdev, instance, cp.data);
|
||||
else
|
||||
len = create_default_scan_rsp_data(hdev, cp.data);
|
||||
|
||||
@@ -900,21 +920,7 @@ static void update_scan_rsp_data_for_instance(struct hci_request *req,
|
||||
|
||||
static void update_scan_rsp_data(struct hci_request *req)
|
||||
{
|
||||
struct hci_dev *hdev = req->hdev;
|
||||
u8 instance;
|
||||
|
||||
/* The "Set Advertising" setting supersedes the "Add Advertising"
|
||||
* setting. Here we set the scan response data based on which
|
||||
* setting was set. When neither apply, default to the global settings,
|
||||
* represented by instance "0".
|
||||
*/
|
||||
if (hci_dev_test_flag(hdev, HCI_ADVERTISING_INSTANCE) &&
|
||||
!hci_dev_test_flag(hdev, HCI_ADVERTISING))
|
||||
instance = 0x01;
|
||||
else
|
||||
instance = 0x00;
|
||||
|
||||
update_scan_rsp_data_for_instance(req, instance);
|
||||
update_inst_scan_rsp_data(req, get_current_adv_instance(req->hdev));
|
||||
}
|
||||
|
||||
static u8 get_adv_discov_flags(struct hci_dev *hdev)
|
||||
@@ -941,20 +947,6 @@ static u8 get_adv_discov_flags(struct hci_dev *hdev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static u8 get_current_adv_instance(struct hci_dev *hdev)
|
||||
{
|
||||
/* The "Set Advertising" setting supersedes the "Add Advertising"
|
||||
* setting. Here we set the advertising data based on which
|
||||
* setting was set. When neither apply, default to the global settings,
|
||||
* represented by instance "0".
|
||||
*/
|
||||
if (hci_dev_test_flag(hdev, HCI_ADVERTISING_INSTANCE) &&
|
||||
!hci_dev_test_flag(hdev, HCI_ADVERTISING))
|
||||
return 0x01;
|
||||
|
||||
return 0x00;
|
||||
}
|
||||
|
||||
static bool get_connectable(struct hci_dev *hdev)
|
||||
{
|
||||
struct mgmt_pending_cmd *cmd;
|
||||
@@ -975,41 +967,65 @@ static bool get_connectable(struct hci_dev *hdev)
|
||||
static u32 get_adv_instance_flags(struct hci_dev *hdev, u8 instance)
|
||||
{
|
||||
u32 flags;
|
||||
struct adv_info *adv_instance;
|
||||
|
||||
if (instance > 0x01)
|
||||
if (instance == 0x00) {
|
||||
/* Instance 0 always manages the "Tx Power" and "Flags"
|
||||
* fields
|
||||
*/
|
||||
flags = MGMT_ADV_FLAG_TX_POWER | MGMT_ADV_FLAG_MANAGED_FLAGS;
|
||||
|
||||
/* For instance 0, the HCI_ADVERTISING_CONNECTABLE setting
|
||||
* corresponds to the "connectable" instance flag.
|
||||
*/
|
||||
if (hci_dev_test_flag(hdev, HCI_ADVERTISING_CONNECTABLE))
|
||||
flags |= MGMT_ADV_FLAG_CONNECTABLE;
|
||||
|
||||
return flags;
|
||||
}
|
||||
|
||||
adv_instance = hci_find_adv_instance(hdev, instance);
|
||||
|
||||
/* Return 0 when we got an invalid instance identifier. */
|
||||
if (!adv_instance)
|
||||
return 0;
|
||||
|
||||
if (instance == 0x01)
|
||||
return hdev->adv_instance.flags;
|
||||
|
||||
/* Instance 0 always manages the "Tx Power" and "Flags" fields */
|
||||
flags = MGMT_ADV_FLAG_TX_POWER | MGMT_ADV_FLAG_MANAGED_FLAGS;
|
||||
|
||||
/* For instance 0, the HCI_ADVERTISING_CONNECTABLE setting corresponds
|
||||
* to the "connectable" instance flag.
|
||||
*/
|
||||
if (hci_dev_test_flag(hdev, HCI_ADVERTISING_CONNECTABLE))
|
||||
flags |= MGMT_ADV_FLAG_CONNECTABLE;
|
||||
|
||||
return flags;
|
||||
return adv_instance->flags;
|
||||
}
|
||||
|
||||
static u8 get_adv_instance_scan_rsp_len(struct hci_dev *hdev, u8 instance)
|
||||
static u8 get_cur_adv_instance_scan_rsp_len(struct hci_dev *hdev)
|
||||
{
|
||||
/* Ignore instance 0 and other unsupported instances */
|
||||
if (instance != 0x01)
|
||||
u8 instance = get_current_adv_instance(hdev);
|
||||
struct adv_info *adv_instance;
|
||||
|
||||
/* Ignore instance 0 */
|
||||
if (instance == 0x00)
|
||||
return 0;
|
||||
|
||||
adv_instance = hci_find_adv_instance(hdev, instance);
|
||||
if (!adv_instance)
|
||||
return 0;
|
||||
|
||||
/* TODO: Take into account the "appearance" and "local-name" flags here.
|
||||
* These are currently being ignored as they are not supported.
|
||||
*/
|
||||
return hdev->adv_instance.scan_rsp_len;
|
||||
return adv_instance->scan_rsp_len;
|
||||
}
|
||||
|
||||
static u8 create_instance_adv_data(struct hci_dev *hdev, u8 instance, u8 *ptr)
|
||||
{
|
||||
struct adv_info *adv_instance = NULL;
|
||||
u8 ad_len = 0, flags = 0;
|
||||
u32 instance_flags = get_adv_instance_flags(hdev, instance);
|
||||
u32 instance_flags;
|
||||
|
||||
/* Return 0 when the current instance identifier is invalid. */
|
||||
if (instance) {
|
||||
adv_instance = hci_find_adv_instance(hdev, instance);
|
||||
if (!adv_instance)
|
||||
return 0;
|
||||
}
|
||||
|
||||
instance_flags = get_adv_instance_flags(hdev, instance);
|
||||
|
||||
/* The Add Advertising command allows userspace to set both the general
|
||||
* and limited discoverable flags.
|
||||
@@ -1043,12 +1059,11 @@ static u8 create_instance_adv_data(struct hci_dev *hdev, u8 instance, u8 *ptr)
|
||||
}
|
||||
}
|
||||
|
||||
if (instance) {
|
||||
memcpy(ptr, hdev->adv_instance.adv_data,
|
||||
hdev->adv_instance.adv_data_len);
|
||||
|
||||
ad_len += hdev->adv_instance.adv_data_len;
|
||||
ptr += hdev->adv_instance.adv_data_len;
|
||||
if (adv_instance) {
|
||||
memcpy(ptr, adv_instance->adv_data,
|
||||
adv_instance->adv_data_len);
|
||||
ad_len += adv_instance->adv_data_len;
|
||||
ptr += adv_instance->adv_data_len;
|
||||
}
|
||||
|
||||
/* Provide Tx Power only if we can provide a valid value for it */
|
||||
@@ -1065,7 +1080,7 @@ static u8 create_instance_adv_data(struct hci_dev *hdev, u8 instance, u8 *ptr)
|
||||
return ad_len;
|
||||
}
|
||||
|
||||
static void update_adv_data_for_instance(struct hci_request *req, u8 instance)
|
||||
static void update_inst_adv_data(struct hci_request *req, u8 instance)
|
||||
{
|
||||
struct hci_dev *hdev = req->hdev;
|
||||
struct hci_cp_le_set_adv_data cp;
|
||||
@@ -1093,10 +1108,7 @@ static void update_adv_data_for_instance(struct hci_request *req, u8 instance)
|
||||
|
||||
static void update_adv_data(struct hci_request *req)
|
||||
{
|
||||
struct hci_dev *hdev = req->hdev;
|
||||
u8 instance = get_current_adv_instance(hdev);
|
||||
|
||||
update_adv_data_for_instance(req, instance);
|
||||
update_inst_adv_data(req, get_current_adv_instance(req->hdev));
|
||||
}
|
||||
|
||||
int mgmt_update_adv_data(struct hci_dev *hdev)
|
||||
@@ -1277,7 +1289,7 @@ static void enable_advertising(struct hci_request *req)
|
||||
|
||||
if (connectable)
|
||||
cp.type = LE_ADV_IND;
|
||||
else if (get_adv_instance_scan_rsp_len(hdev, instance))
|
||||
else if (get_cur_adv_instance_scan_rsp_len(hdev))
|
||||
cp.type = LE_ADV_SCAN_IND;
|
||||
else
|
||||
cp.type = LE_ADV_NONCONN_IND;
|
||||
@@ -1459,27 +1471,141 @@ static void advertising_removed(struct sock *sk, struct hci_dev *hdev,
|
||||
mgmt_event(MGMT_EV_ADVERTISING_REMOVED, hdev, &ev, sizeof(ev), sk);
|
||||
}
|
||||
|
||||
static void clear_adv_instance(struct hci_dev *hdev)
|
||||
static int schedule_adv_instance(struct hci_request *req, u8 instance,
|
||||
bool force) {
|
||||
struct hci_dev *hdev = req->hdev;
|
||||
struct adv_info *adv_instance = NULL;
|
||||
u16 timeout;
|
||||
|
||||
if (hci_dev_test_flag(hdev, HCI_ADVERTISING) ||
|
||||
!hci_dev_test_flag(hdev, HCI_ADVERTISING_INSTANCE))
|
||||
return -EPERM;
|
||||
|
||||
if (hdev->adv_instance_timeout)
|
||||
return -EBUSY;
|
||||
|
||||
adv_instance = hci_find_adv_instance(hdev, instance);
|
||||
if (!adv_instance)
|
||||
return -ENOENT;
|
||||
|
||||
/* A zero timeout means unlimited advertising. As long as there is
|
||||
* only one instance, duration should be ignored. We still set a timeout
|
||||
* in case further instances are being added later on.
|
||||
*
|
||||
* If the remaining lifetime of the instance is more than the duration
|
||||
* then the timeout corresponds to the duration, otherwise it will be
|
||||
* reduced to the remaining instance lifetime.
|
||||
*/
|
||||
if (adv_instance->timeout == 0 ||
|
||||
adv_instance->duration <= adv_instance->remaining_time)
|
||||
timeout = adv_instance->duration;
|
||||
else
|
||||
timeout = adv_instance->remaining_time;
|
||||
|
||||
/* The remaining time is being reduced unless the instance is being
|
||||
* advertised without time limit.
|
||||
*/
|
||||
if (adv_instance->timeout)
|
||||
adv_instance->remaining_time =
|
||||
adv_instance->remaining_time - timeout;
|
||||
|
||||
hdev->adv_instance_timeout = timeout;
|
||||
queue_delayed_work(hdev->workqueue,
|
||||
&hdev->adv_instance_expire,
|
||||
msecs_to_jiffies(timeout * 1000));
|
||||
|
||||
/* If we're just re-scheduling the same instance again then do not
|
||||
* execute any HCI commands. This happens when a single instance is
|
||||
* being advertised.
|
||||
*/
|
||||
if (!force && hdev->cur_adv_instance == instance &&
|
||||
hci_dev_test_flag(hdev, HCI_LE_ADV))
|
||||
return 0;
|
||||
|
||||
hdev->cur_adv_instance = instance;
|
||||
update_adv_data(req);
|
||||
update_scan_rsp_data(req);
|
||||
enable_advertising(req);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void cancel_adv_timeout(struct hci_dev *hdev)
|
||||
{
|
||||
struct hci_request req;
|
||||
if (hdev->adv_instance_timeout) {
|
||||
hdev->adv_instance_timeout = 0;
|
||||
cancel_delayed_work(&hdev->adv_instance_expire);
|
||||
}
|
||||
}
|
||||
|
||||
if (!hci_dev_test_flag(hdev, HCI_ADVERTISING_INSTANCE))
|
||||
return;
|
||||
/* For a single instance:
|
||||
* - force == true: The instance will be removed even when its remaining
|
||||
* lifetime is not zero.
|
||||
* - force == false: the instance will be deactivated but kept stored unless
|
||||
* the remaining lifetime is zero.
|
||||
*
|
||||
* For instance == 0x00:
|
||||
* - force == true: All instances will be removed regardless of their timeout
|
||||
* setting.
|
||||
* - force == false: Only instances that have a timeout will be removed.
|
||||
*/
|
||||
static void clear_adv_instance(struct hci_dev *hdev, struct hci_request *req,
|
||||
u8 instance, bool force)
|
||||
{
|
||||
struct adv_info *adv_instance, *n, *next_instance = NULL;
|
||||
int err;
|
||||
u8 rem_inst;
|
||||
|
||||
if (hdev->adv_instance.timeout)
|
||||
cancel_delayed_work(&hdev->adv_instance.timeout_exp);
|
||||
/* Cancel any timeout concerning the removed instance(s). */
|
||||
if (!instance || hdev->cur_adv_instance == instance)
|
||||
cancel_adv_timeout(hdev);
|
||||
|
||||
memset(&hdev->adv_instance, 0, sizeof(hdev->adv_instance));
|
||||
advertising_removed(NULL, hdev, 1);
|
||||
hci_dev_clear_flag(hdev, HCI_ADVERTISING_INSTANCE);
|
||||
/* Get the next instance to advertise BEFORE we remove
|
||||
* the current one. This can be the same instance again
|
||||
* if there is only one instance.
|
||||
*/
|
||||
if (instance && hdev->cur_adv_instance == instance)
|
||||
next_instance = hci_get_next_instance(hdev, instance);
|
||||
|
||||
if (!hdev_is_powered(hdev) ||
|
||||
if (instance == 0x00) {
|
||||
list_for_each_entry_safe(adv_instance, n, &hdev->adv_instances,
|
||||
list) {
|
||||
if (!(force || adv_instance->timeout))
|
||||
continue;
|
||||
|
||||
rem_inst = adv_instance->instance;
|
||||
err = hci_remove_adv_instance(hdev, rem_inst);
|
||||
if (!err)
|
||||
advertising_removed(NULL, hdev, rem_inst);
|
||||
}
|
||||
hdev->cur_adv_instance = 0x00;
|
||||
} else {
|
||||
adv_instance = hci_find_adv_instance(hdev, instance);
|
||||
|
||||
if (force || (adv_instance && adv_instance->timeout &&
|
||||
!adv_instance->remaining_time)) {
|
||||
/* Don't advertise a removed instance. */
|
||||
if (next_instance &&
|
||||
next_instance->instance == instance)
|
||||
next_instance = NULL;
|
||||
|
||||
err = hci_remove_adv_instance(hdev, instance);
|
||||
if (!err)
|
||||
advertising_removed(NULL, hdev, instance);
|
||||
}
|
||||
}
|
||||
|
||||
if (list_empty(&hdev->adv_instances)) {
|
||||
hdev->cur_adv_instance = 0x00;
|
||||
hci_dev_clear_flag(hdev, HCI_ADVERTISING_INSTANCE);
|
||||
}
|
||||
|
||||
if (!req || !hdev_is_powered(hdev) ||
|
||||
hci_dev_test_flag(hdev, HCI_ADVERTISING))
|
||||
return;
|
||||
|
||||
hci_req_init(&req, hdev);
|
||||
disable_advertising(&req);
|
||||
hci_req_run(&req, NULL);
|
||||
if (next_instance)
|
||||
schedule_adv_instance(req, next_instance->instance, false);
|
||||
}
|
||||
|
||||
static int clean_up_hci_state(struct hci_dev *hdev)
|
||||
@@ -1497,8 +1623,7 @@ static int clean_up_hci_state(struct hci_dev *hdev)
|
||||
hci_req_add(&req, HCI_OP_WRITE_SCAN_ENABLE, 1, &scan);
|
||||
}
|
||||
|
||||
if (hdev->adv_instance.timeout)
|
||||
clear_adv_instance(hdev);
|
||||
clear_adv_instance(hdev, NULL, 0x00, false);
|
||||
|
||||
if (hci_dev_test_flag(hdev, HCI_LE_ADV))
|
||||
disable_advertising(&req);
|
||||
@@ -2453,6 +2578,9 @@ static int set_le(struct sock *sk, struct hci_dev *hdev, void *data, u16 len)
|
||||
val = !!cp->val;
|
||||
enabled = lmp_host_le_capable(hdev);
|
||||
|
||||
if (!val)
|
||||
clear_adv_instance(hdev, NULL, 0x00, true);
|
||||
|
||||
if (!hdev_is_powered(hdev) || val == enabled) {
|
||||
bool changed = false;
|
||||
|
||||
@@ -4087,6 +4215,7 @@ static bool trigger_le_scan(struct hci_request *req, u16 interval, u8 *status)
|
||||
return false;
|
||||
}
|
||||
|
||||
cancel_adv_timeout(hdev);
|
||||
disable_advertising(req);
|
||||
}
|
||||
|
||||
@@ -4669,6 +4798,9 @@ static void set_advertising_complete(struct hci_dev *hdev, u8 status,
|
||||
{
|
||||
struct cmd_lookup match = { NULL, hdev };
|
||||
struct hci_request req;
|
||||
u8 instance;
|
||||
struct adv_info *adv_instance;
|
||||
int err;
|
||||
|
||||
hci_dev_lock(hdev);
|
||||
|
||||
@@ -4694,18 +4826,31 @@ static void set_advertising_complete(struct hci_dev *hdev, u8 status,
|
||||
sock_put(match.sk);
|
||||
|
||||
/* If "Set Advertising" was just disabled and instance advertising was
|
||||
* set up earlier, then enable the advertising instance.
|
||||
* set up earlier, then re-enable multi-instance advertising.
|
||||
*/
|
||||
if (hci_dev_test_flag(hdev, HCI_ADVERTISING) ||
|
||||
!hci_dev_test_flag(hdev, HCI_ADVERTISING_INSTANCE))
|
||||
!hci_dev_test_flag(hdev, HCI_ADVERTISING_INSTANCE) ||
|
||||
list_empty(&hdev->adv_instances))
|
||||
goto unlock;
|
||||
|
||||
instance = hdev->cur_adv_instance;
|
||||
if (!instance) {
|
||||
adv_instance = list_first_entry_or_null(&hdev->adv_instances,
|
||||
struct adv_info, list);
|
||||
if (!adv_instance)
|
||||
goto unlock;
|
||||
|
||||
instance = adv_instance->instance;
|
||||
}
|
||||
|
||||
hci_req_init(&req, hdev);
|
||||
|
||||
update_adv_data(&req);
|
||||
enable_advertising(&req);
|
||||
err = schedule_adv_instance(&req, instance, true);
|
||||
|
||||
if (hci_req_run(&req, enable_advertising_instance) < 0)
|
||||
if (!err)
|
||||
err = hci_req_run(&req, enable_advertising_instance);
|
||||
|
||||
if (err)
|
||||
BT_ERR("Failed to re-configure advertising");
|
||||
|
||||
unlock:
|
||||
@@ -4790,10 +4935,15 @@ static int set_advertising(struct sock *sk, struct hci_dev *hdev, void *data,
|
||||
else
|
||||
hci_dev_clear_flag(hdev, HCI_ADVERTISING_CONNECTABLE);
|
||||
|
||||
cancel_adv_timeout(hdev);
|
||||
|
||||
if (val) {
|
||||
/* Switch to instance "0" for the Set Advertising setting. */
|
||||
update_adv_data_for_instance(&req, 0);
|
||||
update_scan_rsp_data_for_instance(&req, 0);
|
||||
/* Switch to instance "0" for the Set Advertising setting.
|
||||
* We cannot use update_[adv|scan_rsp]_data() here as the
|
||||
* HCI_ADVERTISING flag is not yet set.
|
||||
*/
|
||||
update_inst_adv_data(&req, 0x00);
|
||||
update_inst_scan_rsp_data(&req, 0x00);
|
||||
enable_advertising(&req);
|
||||
} else {
|
||||
disable_advertising(&req);
|
||||
@@ -6781,8 +6931,9 @@ static int read_adv_features(struct sock *sk, struct hci_dev *hdev,
|
||||
{
|
||||
struct mgmt_rp_read_adv_features *rp;
|
||||
size_t rp_len;
|
||||
int err;
|
||||
int err, i;
|
||||
bool instance;
|
||||
struct adv_info *adv_instance;
|
||||
u32 supported_flags;
|
||||
|
||||
BT_DBG("%s", hdev->name);
|
||||
@@ -6795,12 +6946,9 @@ static int read_adv_features(struct sock *sk, struct hci_dev *hdev,
|
||||
|
||||
rp_len = sizeof(*rp);
|
||||
|
||||
/* Currently only one instance is supported, so just add 1 to the
|
||||
* response length.
|
||||
*/
|
||||
instance = hci_dev_test_flag(hdev, HCI_ADVERTISING_INSTANCE);
|
||||
if (instance)
|
||||
rp_len++;
|
||||
rp_len += hdev->adv_instance_cnt;
|
||||
|
||||
rp = kmalloc(rp_len, GFP_ATOMIC);
|
||||
if (!rp) {
|
||||
@@ -6813,14 +6961,18 @@ static int read_adv_features(struct sock *sk, struct hci_dev *hdev,
|
||||
rp->supported_flags = cpu_to_le32(supported_flags);
|
||||
rp->max_adv_data_len = HCI_MAX_AD_LENGTH;
|
||||
rp->max_scan_rsp_len = HCI_MAX_AD_LENGTH;
|
||||
rp->max_instances = 1;
|
||||
rp->max_instances = HCI_MAX_ADV_INSTANCES;
|
||||
|
||||
/* Currently only one instance is supported, so simply return the
|
||||
* current instance number.
|
||||
*/
|
||||
if (instance) {
|
||||
rp->num_instances = 1;
|
||||
rp->instance[0] = 1;
|
||||
i = 0;
|
||||
list_for_each_entry(adv_instance, &hdev->adv_instances, list) {
|
||||
if (i >= hdev->adv_instance_cnt)
|
||||
break;
|
||||
|
||||
rp->instance[i] = adv_instance->instance;
|
||||
i++;
|
||||
}
|
||||
rp->num_instances = hdev->adv_instance_cnt;
|
||||
} else {
|
||||
rp->num_instances = 0;
|
||||
}
|
||||
@@ -6882,7 +7034,10 @@ static void add_advertising_complete(struct hci_dev *hdev, u8 status,
|
||||
u16 opcode)
|
||||
{
|
||||
struct mgmt_pending_cmd *cmd;
|
||||
struct mgmt_cp_add_advertising *cp;
|
||||
struct mgmt_rp_add_advertising rp;
|
||||
struct adv_info *adv_instance, *n;
|
||||
u8 instance;
|
||||
|
||||
BT_DBG("status %d", status);
|
||||
|
||||
@@ -6890,16 +7045,32 @@ static void add_advertising_complete(struct hci_dev *hdev, u8 status,
|
||||
|
||||
cmd = pending_find(MGMT_OP_ADD_ADVERTISING, hdev);
|
||||
|
||||
if (status) {
|
||||
if (status)
|
||||
hci_dev_clear_flag(hdev, HCI_ADVERTISING_INSTANCE);
|
||||
memset(&hdev->adv_instance, 0, sizeof(hdev->adv_instance));
|
||||
advertising_removed(cmd ? cmd->sk : NULL, hdev, 1);
|
||||
|
||||
list_for_each_entry_safe(adv_instance, n, &hdev->adv_instances, list) {
|
||||
if (!adv_instance->pending)
|
||||
continue;
|
||||
|
||||
if (!status) {
|
||||
adv_instance->pending = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
instance = adv_instance->instance;
|
||||
|
||||
if (hdev->cur_adv_instance == instance)
|
||||
cancel_adv_timeout(hdev);
|
||||
|
||||
hci_remove_adv_instance(hdev, instance);
|
||||
advertising_removed(cmd ? cmd->sk : NULL, hdev, instance);
|
||||
}
|
||||
|
||||
if (!cmd)
|
||||
goto unlock;
|
||||
|
||||
rp.instance = 0x01;
|
||||
cp = cmd->param;
|
||||
rp.instance = cp->instance;
|
||||
|
||||
if (status)
|
||||
mgmt_cmd_status(cmd->sk, cmd->index, cmd->opcode,
|
||||
@@ -6914,15 +7085,28 @@ static void add_advertising_complete(struct hci_dev *hdev, u8 status,
|
||||
hci_dev_unlock(hdev);
|
||||
}
|
||||
|
||||
static void adv_timeout_expired(struct work_struct *work)
|
||||
void mgmt_adv_timeout_expired(struct hci_dev *hdev)
|
||||
{
|
||||
struct hci_dev *hdev = container_of(work, struct hci_dev,
|
||||
adv_instance.timeout_exp.work);
|
||||
u8 instance;
|
||||
struct hci_request req;
|
||||
|
||||
hdev->adv_instance.timeout = 0;
|
||||
hdev->adv_instance_timeout = 0;
|
||||
|
||||
instance = get_current_adv_instance(hdev);
|
||||
if (instance == 0x00)
|
||||
return;
|
||||
|
||||
hci_dev_lock(hdev);
|
||||
clear_adv_instance(hdev);
|
||||
hci_req_init(&req, hdev);
|
||||
|
||||
clear_adv_instance(hdev, &req, instance, false);
|
||||
|
||||
if (list_empty(&hdev->adv_instances))
|
||||
disable_advertising(&req);
|
||||
|
||||
if (!skb_queue_empty(&req.cmd_q))
|
||||
hci_req_run(&req, NULL);
|
||||
|
||||
hci_dev_unlock(hdev);
|
||||
}
|
||||
|
||||
@@ -6934,7 +7118,10 @@ static int add_advertising(struct sock *sk, struct hci_dev *hdev,
|
||||
u32 flags;
|
||||
u32 supported_flags;
|
||||
u8 status;
|
||||
u16 timeout;
|
||||
u16 timeout, duration;
|
||||
unsigned int prev_instance_cnt = hdev->adv_instance_cnt;
|
||||
u8 schedule_instance = 0;
|
||||
struct adv_info *next_instance;
|
||||
int err;
|
||||
struct mgmt_pending_cmd *cmd;
|
||||
struct hci_request req;
|
||||
@@ -6948,12 +7135,13 @@ static int add_advertising(struct sock *sk, struct hci_dev *hdev,
|
||||
|
||||
flags = __le32_to_cpu(cp->flags);
|
||||
timeout = __le16_to_cpu(cp->timeout);
|
||||
duration = __le16_to_cpu(cp->duration);
|
||||
|
||||
/* The current implementation only supports adding one instance and only
|
||||
* a subset of the specified flags.
|
||||
/* The current implementation only supports a subset of the specified
|
||||
* flags.
|
||||
*/
|
||||
supported_flags = get_supported_adv_flags(hdev);
|
||||
if (cp->instance != 0x01 || (flags & ~supported_flags))
|
||||
if (flags & ~supported_flags)
|
||||
return mgmt_cmd_status(sk, hdev->id, MGMT_OP_ADD_ADVERTISING,
|
||||
MGMT_STATUS_INVALID_PARAMS);
|
||||
|
||||
@@ -6981,38 +7169,51 @@ static int add_advertising(struct sock *sk, struct hci_dev *hdev,
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
INIT_DELAYED_WORK(&hdev->adv_instance.timeout_exp, adv_timeout_expired);
|
||||
err = hci_add_adv_instance(hdev, cp->instance, flags,
|
||||
cp->adv_data_len, cp->data,
|
||||
cp->scan_rsp_len,
|
||||
cp->data + cp->adv_data_len,
|
||||
timeout, duration);
|
||||
if (err < 0) {
|
||||
err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_ADD_ADVERTISING,
|
||||
MGMT_STATUS_FAILED);
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
hdev->adv_instance.flags = flags;
|
||||
hdev->adv_instance.adv_data_len = cp->adv_data_len;
|
||||
hdev->adv_instance.scan_rsp_len = cp->scan_rsp_len;
|
||||
/* Only trigger an advertising added event if a new instance was
|
||||
* actually added.
|
||||
*/
|
||||
if (hdev->adv_instance_cnt > prev_instance_cnt)
|
||||
advertising_added(sk, hdev, cp->instance);
|
||||
|
||||
if (cp->adv_data_len)
|
||||
memcpy(hdev->adv_instance.adv_data, cp->data, cp->adv_data_len);
|
||||
hci_dev_set_flag(hdev, HCI_ADVERTISING_INSTANCE);
|
||||
|
||||
if (cp->scan_rsp_len)
|
||||
memcpy(hdev->adv_instance.scan_rsp_data,
|
||||
cp->data + cp->adv_data_len, cp->scan_rsp_len);
|
||||
if (hdev->cur_adv_instance == cp->instance) {
|
||||
/* If the currently advertised instance is being changed then
|
||||
* cancel the current advertising and schedule the next
|
||||
* instance. If there is only one instance then the overridden
|
||||
* advertising data will be visible right away.
|
||||
*/
|
||||
cancel_adv_timeout(hdev);
|
||||
|
||||
if (hdev->adv_instance.timeout)
|
||||
cancel_delayed_work(&hdev->adv_instance.timeout_exp);
|
||||
next_instance = hci_get_next_instance(hdev, cp->instance);
|
||||
if (next_instance)
|
||||
schedule_instance = next_instance->instance;
|
||||
} else if (!hdev->adv_instance_timeout) {
|
||||
/* Immediately advertise the new instance if no other
|
||||
* instance is currently being advertised.
|
||||
*/
|
||||
schedule_instance = cp->instance;
|
||||
}
|
||||
|
||||
hdev->adv_instance.timeout = timeout;
|
||||
|
||||
if (timeout)
|
||||
queue_delayed_work(hdev->workqueue,
|
||||
&hdev->adv_instance.timeout_exp,
|
||||
msecs_to_jiffies(timeout * 1000));
|
||||
|
||||
if (!hci_dev_test_and_set_flag(hdev, HCI_ADVERTISING_INSTANCE))
|
||||
advertising_added(sk, hdev, 1);
|
||||
|
||||
/* If the HCI_ADVERTISING flag is set or the device isn't powered then
|
||||
* we have no HCI communication to make. Simply return.
|
||||
/* If the HCI_ADVERTISING flag is set or the device isn't powered or
|
||||
* there is no instance to be advertised then we have no HCI
|
||||
* communication to make. Simply return.
|
||||
*/
|
||||
if (!hdev_is_powered(hdev) ||
|
||||
hci_dev_test_flag(hdev, HCI_ADVERTISING)) {
|
||||
rp.instance = 0x01;
|
||||
hci_dev_test_flag(hdev, HCI_ADVERTISING) ||
|
||||
!schedule_instance) {
|
||||
rp.instance = cp->instance;
|
||||
err = mgmt_cmd_complete(sk, hdev->id, MGMT_OP_ADD_ADVERTISING,
|
||||
MGMT_STATUS_SUCCESS, &rp, sizeof(rp));
|
||||
goto unlock;
|
||||
@@ -7030,11 +7231,11 @@ static int add_advertising(struct sock *sk, struct hci_dev *hdev,
|
||||
|
||||
hci_req_init(&req, hdev);
|
||||
|
||||
update_adv_data(&req);
|
||||
update_scan_rsp_data(&req);
|
||||
enable_advertising(&req);
|
||||
err = schedule_adv_instance(&req, schedule_instance, true);
|
||||
|
||||
if (!err)
|
||||
err = hci_req_run(&req, add_advertising_complete);
|
||||
|
||||
err = hci_req_run(&req, add_advertising_complete);
|
||||
if (err < 0)
|
||||
mgmt_pending_remove(cmd);
|
||||
|
||||
@@ -7048,6 +7249,7 @@ static void remove_advertising_complete(struct hci_dev *hdev, u8 status,
|
||||
u16 opcode)
|
||||
{
|
||||
struct mgmt_pending_cmd *cmd;
|
||||
struct mgmt_cp_remove_advertising *cp;
|
||||
struct mgmt_rp_remove_advertising rp;
|
||||
|
||||
BT_DBG("status %d", status);
|
||||
@@ -7062,7 +7264,8 @@ static void remove_advertising_complete(struct hci_dev *hdev, u8 status,
|
||||
if (!cmd)
|
||||
goto unlock;
|
||||
|
||||
rp.instance = 1;
|
||||
cp = cmd->param;
|
||||
rp.instance = cp->instance;
|
||||
|
||||
mgmt_cmd_complete(cmd->sk, cmd->index, cmd->opcode, MGMT_STATUS_SUCCESS,
|
||||
&rp, sizeof(rp));
|
||||
@@ -7077,21 +7280,21 @@ static int remove_advertising(struct sock *sk, struct hci_dev *hdev,
|
||||
{
|
||||
struct mgmt_cp_remove_advertising *cp = data;
|
||||
struct mgmt_rp_remove_advertising rp;
|
||||
int err;
|
||||
struct mgmt_pending_cmd *cmd;
|
||||
struct hci_request req;
|
||||
int err;
|
||||
|
||||
BT_DBG("%s", hdev->name);
|
||||
|
||||
/* The current implementation only allows modifying instance no 1. A
|
||||
* value of 0 indicates that all instances should be cleared.
|
||||
*/
|
||||
if (cp->instance > 1)
|
||||
return mgmt_cmd_status(sk, hdev->id, MGMT_OP_REMOVE_ADVERTISING,
|
||||
MGMT_STATUS_INVALID_PARAMS);
|
||||
|
||||
hci_dev_lock(hdev);
|
||||
|
||||
if (cp->instance && !hci_find_adv_instance(hdev, cp->instance)) {
|
||||
err = mgmt_cmd_status(sk, hdev->id,
|
||||
MGMT_OP_REMOVE_ADVERTISING,
|
||||
MGMT_STATUS_INVALID_PARAMS);
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
if (pending_find(MGMT_OP_ADD_ADVERTISING, hdev) ||
|
||||
pending_find(MGMT_OP_REMOVE_ADVERTISING, hdev) ||
|
||||
pending_find(MGMT_OP_SET_LE, hdev)) {
|
||||
@@ -7106,21 +7309,21 @@ static int remove_advertising(struct sock *sk, struct hci_dev *hdev,
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
if (hdev->adv_instance.timeout)
|
||||
cancel_delayed_work(&hdev->adv_instance.timeout_exp);
|
||||
hci_req_init(&req, hdev);
|
||||
|
||||
memset(&hdev->adv_instance, 0, sizeof(hdev->adv_instance));
|
||||
clear_adv_instance(hdev, &req, cp->instance, true);
|
||||
|
||||
advertising_removed(sk, hdev, 1);
|
||||
if (list_empty(&hdev->adv_instances))
|
||||
disable_advertising(&req);
|
||||
|
||||
hci_dev_clear_flag(hdev, HCI_ADVERTISING_INSTANCE);
|
||||
|
||||
/* If the HCI_ADVERTISING flag is set or the device isn't powered then
|
||||
* we have no HCI communication to make. Simply return.
|
||||
/* If no HCI commands have been collected so far or the HCI_ADVERTISING
|
||||
* flag is set or the device isn't powered then we have no HCI
|
||||
* communication to make. Simply return.
|
||||
*/
|
||||
if (!hdev_is_powered(hdev) ||
|
||||
if (skb_queue_empty(&req.cmd_q) ||
|
||||
!hdev_is_powered(hdev) ||
|
||||
hci_dev_test_flag(hdev, HCI_ADVERTISING)) {
|
||||
rp.instance = 1;
|
||||
rp.instance = cp->instance;
|
||||
err = mgmt_cmd_complete(sk, hdev->id,
|
||||
MGMT_OP_REMOVE_ADVERTISING,
|
||||
MGMT_STATUS_SUCCESS, &rp, sizeof(rp));
|
||||
@@ -7134,9 +7337,6 @@ static int remove_advertising(struct sock *sk, struct hci_dev *hdev,
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
hci_req_init(&req, hdev);
|
||||
disable_advertising(&req);
|
||||
|
||||
err = hci_req_run(&req, remove_advertising_complete);
|
||||
if (err < 0)
|
||||
mgmt_pending_remove(cmd);
|
||||
@@ -7361,6 +7561,7 @@ static void powered_complete(struct hci_dev *hdev, u8 status, u16 opcode)
|
||||
static int powered_update_hci(struct hci_dev *hdev)
|
||||
{
|
||||
struct hci_request req;
|
||||
struct adv_info *adv_instance;
|
||||
u8 link_sec;
|
||||
|
||||
hci_req_init(&req, hdev);
|
||||
@@ -7400,14 +7601,27 @@ static int powered_update_hci(struct hci_dev *hdev)
|
||||
* advertising data. This also applies to the case
|
||||
* where BR/EDR was toggled during the AUTO_OFF phase.
|
||||
*/
|
||||
if (hci_dev_test_flag(hdev, HCI_LE_ENABLED)) {
|
||||
if (hci_dev_test_flag(hdev, HCI_LE_ENABLED) &&
|
||||
(hci_dev_test_flag(hdev, HCI_ADVERTISING) ||
|
||||
!hci_dev_test_flag(hdev, HCI_ADVERTISING_INSTANCE))) {
|
||||
update_adv_data(&req);
|
||||
update_scan_rsp_data(&req);
|
||||
}
|
||||
|
||||
if (hci_dev_test_flag(hdev, HCI_ADVERTISING) ||
|
||||
hci_dev_test_flag(hdev, HCI_ADVERTISING_INSTANCE))
|
||||
if (hci_dev_test_flag(hdev, HCI_ADVERTISING_INSTANCE) &&
|
||||
hdev->cur_adv_instance == 0x00 &&
|
||||
!list_empty(&hdev->adv_instances)) {
|
||||
adv_instance = list_first_entry(&hdev->adv_instances,
|
||||
struct adv_info, list);
|
||||
hdev->cur_adv_instance = adv_instance->instance;
|
||||
}
|
||||
|
||||
if (hci_dev_test_flag(hdev, HCI_ADVERTISING))
|
||||
enable_advertising(&req);
|
||||
else if (hci_dev_test_flag(hdev, HCI_ADVERTISING_INSTANCE) &&
|
||||
hdev->cur_adv_instance)
|
||||
schedule_adv_instance(&req, hdev->cur_adv_instance,
|
||||
true);
|
||||
|
||||
restart_le_actions(&req);
|
||||
}
|
||||
@@ -7577,7 +7791,7 @@ void mgmt_new_ltk(struct hci_dev *hdev, struct smp_ltk *key, bool persistent)
|
||||
memset(&ev, 0, sizeof(ev));
|
||||
|
||||
/* Devices using resolvable or non-resolvable random addresses
|
||||
* without providing an indentity resolving key don't require
|
||||
* without providing an identity resolving key don't require
|
||||
* to store long term keys. Their addresses will change the
|
||||
* next time around.
|
||||
*
|
||||
@@ -7603,7 +7817,12 @@ void mgmt_new_ltk(struct hci_dev *hdev, struct smp_ltk *key, bool persistent)
|
||||
if (key->type == SMP_LTK)
|
||||
ev.key.master = 1;
|
||||
|
||||
memcpy(ev.key.val, key->val, sizeof(key->val));
|
||||
/* Make sure we copy only the significant bytes based on the
|
||||
* encryption key size, and set the rest of the value to zeroes.
|
||||
*/
|
||||
memcpy(ev.key.val, key->val, sizeof(key->enc_size));
|
||||
memset(ev.key.val + key->enc_size, 0,
|
||||
sizeof(ev.key.val) - key->enc_size);
|
||||
|
||||
mgmt_event(MGMT_EV_NEW_LONG_TERM_KEY, hdev, &ev, sizeof(ev), NULL);
|
||||
}
|
||||
@@ -7617,7 +7836,7 @@ void mgmt_new_irk(struct hci_dev *hdev, struct smp_irk *irk)
|
||||
/* For identity resolving keys from devices that are already
|
||||
* using a public address or static random address, do not
|
||||
* ask for storing this key. The identity resolving key really
|
||||
* is only mandatory for devices using resovlable random
|
||||
* is only mandatory for devices using resolvable random
|
||||
* addresses.
|
||||
*
|
||||
* Storing all identity resolving keys has the downside that
|
||||
@@ -7646,7 +7865,7 @@ void mgmt_new_csrk(struct hci_dev *hdev, struct smp_csrk *csrk,
|
||||
memset(&ev, 0, sizeof(ev));
|
||||
|
||||
/* Devices using resolvable or non-resolvable random addresses
|
||||
* without providing an indentity resolving key don't require
|
||||
* without providing an identity resolving key don't require
|
||||
* to store signature resolving keys. Their addresses will change
|
||||
* the next time around.
|
||||
*
|
||||
@@ -8387,13 +8606,24 @@ static void adv_enable_complete(struct hci_dev *hdev, u8 status, u16 opcode)
|
||||
void mgmt_reenable_advertising(struct hci_dev *hdev)
|
||||
{
|
||||
struct hci_request req;
|
||||
u8 instance;
|
||||
|
||||
if (!hci_dev_test_flag(hdev, HCI_ADVERTISING) &&
|
||||
!hci_dev_test_flag(hdev, HCI_ADVERTISING_INSTANCE))
|
||||
return;
|
||||
|
||||
instance = get_current_adv_instance(hdev);
|
||||
|
||||
hci_req_init(&req, hdev);
|
||||
enable_advertising(&req);
|
||||
|
||||
if (instance) {
|
||||
schedule_adv_instance(&req, instance, true);
|
||||
} else {
|
||||
update_adv_data(&req);
|
||||
update_scan_rsp_data(&req);
|
||||
enable_advertising(&req);
|
||||
}
|
||||
|
||||
hci_req_run(&req, adv_enable_complete);
|
||||
}
|
||||
|
||||
|
||||
@@ -200,7 +200,7 @@ static int rfcomm_l2sock_create(struct socket **sock)
|
||||
|
||||
BT_DBG("");
|
||||
|
||||
err = sock_create_kern(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_L2CAP, sock);
|
||||
err = sock_create_kern(&init_net, PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_L2CAP, sock);
|
||||
if (!err) {
|
||||
struct sock *sk = (*sock)->sk;
|
||||
sk->sk_data_ready = rfcomm_l2data_ready;
|
||||
|
||||
@@ -269,12 +269,12 @@ static struct proto rfcomm_proto = {
|
||||
.obj_size = sizeof(struct rfcomm_pinfo)
|
||||
};
|
||||
|
||||
static struct sock *rfcomm_sock_alloc(struct net *net, struct socket *sock, int proto, gfp_t prio)
|
||||
static struct sock *rfcomm_sock_alloc(struct net *net, struct socket *sock, int proto, gfp_t prio, int kern)
|
||||
{
|
||||
struct rfcomm_dlc *d;
|
||||
struct sock *sk;
|
||||
|
||||
sk = sk_alloc(net, PF_BLUETOOTH, prio, &rfcomm_proto);
|
||||
sk = sk_alloc(net, PF_BLUETOOTH, prio, &rfcomm_proto, kern);
|
||||
if (!sk)
|
||||
return NULL;
|
||||
|
||||
@@ -324,7 +324,7 @@ static int rfcomm_sock_create(struct net *net, struct socket *sock,
|
||||
|
||||
sock->ops = &rfcomm_sock_ops;
|
||||
|
||||
sk = rfcomm_sock_alloc(net, sock, protocol, GFP_ATOMIC);
|
||||
sk = rfcomm_sock_alloc(net, sock, protocol, GFP_ATOMIC, kern);
|
||||
if (!sk)
|
||||
return -ENOMEM;
|
||||
|
||||
@@ -334,16 +334,19 @@ static int rfcomm_sock_create(struct net *net, struct socket *sock,
|
||||
|
||||
static int rfcomm_sock_bind(struct socket *sock, struct sockaddr *addr, int addr_len)
|
||||
{
|
||||
struct sockaddr_rc *sa = (struct sockaddr_rc *) addr;
|
||||
struct sockaddr_rc sa;
|
||||
struct sock *sk = sock->sk;
|
||||
int chan = sa->rc_channel;
|
||||
int err = 0;
|
||||
|
||||
BT_DBG("sk %p %pMR", sk, &sa->rc_bdaddr);
|
||||
int len, err = 0;
|
||||
|
||||
if (!addr || addr->sa_family != AF_BLUETOOTH)
|
||||
return -EINVAL;
|
||||
|
||||
memset(&sa, 0, sizeof(sa));
|
||||
len = min_t(unsigned int, sizeof(sa), addr_len);
|
||||
memcpy(&sa, addr, len);
|
||||
|
||||
BT_DBG("sk %p %pMR", sk, &sa.rc_bdaddr);
|
||||
|
||||
lock_sock(sk);
|
||||
|
||||
if (sk->sk_state != BT_OPEN) {
|
||||
@@ -358,12 +361,13 @@ static int rfcomm_sock_bind(struct socket *sock, struct sockaddr *addr, int addr
|
||||
|
||||
write_lock(&rfcomm_sk_list.lock);
|
||||
|
||||
if (chan && __rfcomm_get_listen_sock_by_addr(chan, &sa->rc_bdaddr)) {
|
||||
if (sa.rc_channel &&
|
||||
__rfcomm_get_listen_sock_by_addr(sa.rc_channel, &sa.rc_bdaddr)) {
|
||||
err = -EADDRINUSE;
|
||||
} else {
|
||||
/* Save source address */
|
||||
bacpy(&rfcomm_pi(sk)->src, &sa->rc_bdaddr);
|
||||
rfcomm_pi(sk)->channel = chan;
|
||||
bacpy(&rfcomm_pi(sk)->src, &sa.rc_bdaddr);
|
||||
rfcomm_pi(sk)->channel = sa.rc_channel;
|
||||
sk->sk_state = BT_BOUND;
|
||||
}
|
||||
|
||||
@@ -969,7 +973,7 @@ int rfcomm_connect_ind(struct rfcomm_session *s, u8 channel, struct rfcomm_dlc *
|
||||
goto done;
|
||||
}
|
||||
|
||||
sk = rfcomm_sock_alloc(sock_net(parent), NULL, BTPROTO_RFCOMM, GFP_ATOMIC);
|
||||
sk = rfcomm_sock_alloc(sock_net(parent), NULL, BTPROTO_RFCOMM, GFP_ATOMIC, 0);
|
||||
if (!sk)
|
||||
goto done;
|
||||
|
||||
|
||||
@@ -460,11 +460,11 @@ static struct proto sco_proto = {
|
||||
.obj_size = sizeof(struct sco_pinfo)
|
||||
};
|
||||
|
||||
static struct sock *sco_sock_alloc(struct net *net, struct socket *sock, int proto, gfp_t prio)
|
||||
static struct sock *sco_sock_alloc(struct net *net, struct socket *sock, int proto, gfp_t prio, int kern)
|
||||
{
|
||||
struct sock *sk;
|
||||
|
||||
sk = sk_alloc(net, PF_BLUETOOTH, prio, &sco_proto);
|
||||
sk = sk_alloc(net, PF_BLUETOOTH, prio, &sco_proto, kern);
|
||||
if (!sk)
|
||||
return NULL;
|
||||
|
||||
@@ -501,7 +501,7 @@ static int sco_sock_create(struct net *net, struct socket *sock, int protocol,
|
||||
|
||||
sock->ops = &sco_sock_ops;
|
||||
|
||||
sk = sco_sock_alloc(net, sock, protocol, GFP_ATOMIC);
|
||||
sk = sco_sock_alloc(net, sock, protocol, GFP_ATOMIC, kern);
|
||||
if (!sk)
|
||||
return -ENOMEM;
|
||||
|
||||
@@ -1026,7 +1026,7 @@ static void sco_conn_ready(struct sco_conn *conn)
|
||||
bh_lock_sock(parent);
|
||||
|
||||
sk = sco_sock_alloc(sock_net(parent), NULL,
|
||||
BTPROTO_SCO, GFP_ATOMIC);
|
||||
BTPROTO_SCO, GFP_ATOMIC, 0);
|
||||
if (!sk) {
|
||||
bh_unlock_sock(parent);
|
||||
sco_conn_unlock(conn);
|
||||
@@ -1110,7 +1110,7 @@ static void sco_disconn_cfm(struct hci_conn *hcon, __u8 reason)
|
||||
sco_conn_del(hcon, bt_to_errno(reason));
|
||||
}
|
||||
|
||||
int sco_recv_scodata(struct hci_conn *hcon, struct sk_buff *skb)
|
||||
void sco_recv_scodata(struct hci_conn *hcon, struct sk_buff *skb)
|
||||
{
|
||||
struct sco_conn *conn = hcon->sco_data;
|
||||
|
||||
@@ -1121,12 +1121,11 @@ int sco_recv_scodata(struct hci_conn *hcon, struct sk_buff *skb)
|
||||
|
||||
if (skb->len) {
|
||||
sco_recv_frame(conn, skb);
|
||||
return 0;
|
||||
return;
|
||||
}
|
||||
|
||||
drop:
|
||||
kfree_skb(skb);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct hci_cb sco_cb = {
|
||||
|
||||
@@ -33,6 +33,9 @@
|
||||
#include "ecc.h"
|
||||
#include "smp.h"
|
||||
|
||||
#define SMP_DEV(hdev) \
|
||||
((struct smp_dev *)((struct l2cap_chan *)((hdev)->smp_data))->data)
|
||||
|
||||
/* Low-level debug macros to be used for stuff that we don't want
|
||||
* accidentially in dmesg, i.e. the values of the various crypto keys
|
||||
* and the inputs & outputs of crypto functions.
|
||||
@@ -81,6 +84,9 @@ struct smp_dev {
|
||||
u8 local_rand[16];
|
||||
bool debug_key;
|
||||
|
||||
u8 min_key_size;
|
||||
u8 max_key_size;
|
||||
|
||||
struct crypto_blkcipher *tfm_aes;
|
||||
struct crypto_hash *tfm_cmac;
|
||||
};
|
||||
@@ -371,6 +377,8 @@ static int smp_e(struct crypto_blkcipher *tfm, const u8 *k, u8 *r)
|
||||
uint8_t tmp[16], data[16];
|
||||
int err;
|
||||
|
||||
SMP_DBG("k %16phN r %16phN", k, r);
|
||||
|
||||
if (!tfm) {
|
||||
BT_ERR("tfm %p", tfm);
|
||||
return -EINVAL;
|
||||
@@ -400,6 +408,8 @@ static int smp_e(struct crypto_blkcipher *tfm, const u8 *k, u8 *r)
|
||||
/* Most significant octet of encryptedData corresponds to data[0] */
|
||||
swap_buf(data, r, 16);
|
||||
|
||||
SMP_DBG("r %16phN", r);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
@@ -410,6 +420,10 @@ static int smp_c1(struct crypto_blkcipher *tfm_aes, const u8 k[16],
|
||||
u8 p1[16], p2[16];
|
||||
int err;
|
||||
|
||||
SMP_DBG("k %16phN r %16phN", k, r);
|
||||
SMP_DBG("iat %u ia %6phN rat %u ra %6phN", _iat, ia, _rat, ra);
|
||||
SMP_DBG("preq %7phN pres %7phN", preq, pres);
|
||||
|
||||
memset(p1, 0, 16);
|
||||
|
||||
/* p1 = pres || preq || _rat || _iat */
|
||||
@@ -418,10 +432,7 @@ static int smp_c1(struct crypto_blkcipher *tfm_aes, const u8 k[16],
|
||||
memcpy(p1 + 2, preq, 7);
|
||||
memcpy(p1 + 9, pres, 7);
|
||||
|
||||
/* p2 = padding || ia || ra */
|
||||
memcpy(p2, ra, 6);
|
||||
memcpy(p2 + 6, ia, 6);
|
||||
memset(p2 + 12, 0, 4);
|
||||
SMP_DBG("p1 %16phN", p1);
|
||||
|
||||
/* res = r XOR p1 */
|
||||
u128_xor((u128 *) res, (u128 *) r, (u128 *) p1);
|
||||
@@ -433,6 +444,13 @@ static int smp_c1(struct crypto_blkcipher *tfm_aes, const u8 k[16],
|
||||
return err;
|
||||
}
|
||||
|
||||
/* p2 = padding || ia || ra */
|
||||
memcpy(p2, ra, 6);
|
||||
memcpy(p2 + 6, ia, 6);
|
||||
memset(p2 + 12, 0, 4);
|
||||
|
||||
SMP_DBG("p2 %16phN", p2);
|
||||
|
||||
/* res = res XOR p2 */
|
||||
u128_xor((u128 *) res, (u128 *) res, (u128 *) p2);
|
||||
|
||||
@@ -696,7 +714,7 @@ static void build_pairing_cmd(struct l2cap_conn *conn,
|
||||
if (rsp == NULL) {
|
||||
req->io_capability = conn->hcon->io_capability;
|
||||
req->oob_flag = oob_flag;
|
||||
req->max_key_size = SMP_MAX_ENC_KEY_SIZE;
|
||||
req->max_key_size = SMP_DEV(hdev)->max_key_size;
|
||||
req->init_key_dist = local_dist;
|
||||
req->resp_key_dist = remote_dist;
|
||||
req->auth_req = (authreq & AUTH_REQ_MASK(hdev));
|
||||
@@ -707,7 +725,7 @@ static void build_pairing_cmd(struct l2cap_conn *conn,
|
||||
|
||||
rsp->io_capability = conn->hcon->io_capability;
|
||||
rsp->oob_flag = oob_flag;
|
||||
rsp->max_key_size = SMP_MAX_ENC_KEY_SIZE;
|
||||
rsp->max_key_size = SMP_DEV(hdev)->max_key_size;
|
||||
rsp->init_key_dist = req->init_key_dist & remote_dist;
|
||||
rsp->resp_key_dist = req->resp_key_dist & local_dist;
|
||||
rsp->auth_req = (authreq & AUTH_REQ_MASK(hdev));
|
||||
@@ -718,10 +736,11 @@ static void build_pairing_cmd(struct l2cap_conn *conn,
|
||||
static u8 check_enc_key_size(struct l2cap_conn *conn, __u8 max_key_size)
|
||||
{
|
||||
struct l2cap_chan *chan = conn->smp;
|
||||
struct hci_dev *hdev = conn->hcon->hdev;
|
||||
struct smp_chan *smp = chan->data;
|
||||
|
||||
if ((max_key_size > SMP_MAX_ENC_KEY_SIZE) ||
|
||||
(max_key_size < SMP_MIN_ENC_KEY_SIZE))
|
||||
if (max_key_size > SMP_DEV(hdev)->max_key_size ||
|
||||
max_key_size < SMP_MIN_ENC_KEY_SIZE)
|
||||
return SMP_ENC_KEY_SIZE;
|
||||
|
||||
smp->enc_key_size = max_key_size;
|
||||
@@ -985,13 +1004,10 @@ static u8 smp_random(struct smp_chan *smp)
|
||||
|
||||
smp_s1(smp->tfm_aes, smp->tk, smp->rrnd, smp->prnd, stk);
|
||||
|
||||
memset(stk + smp->enc_key_size, 0,
|
||||
SMP_MAX_ENC_KEY_SIZE - smp->enc_key_size);
|
||||
|
||||
if (test_and_set_bit(HCI_CONN_ENCRYPT_PEND, &hcon->flags))
|
||||
return SMP_UNSPECIFIED;
|
||||
|
||||
hci_le_start_enc(hcon, ediv, rand, stk);
|
||||
hci_le_start_enc(hcon, ediv, rand, stk, smp->enc_key_size);
|
||||
hcon->enc_key_size = smp->enc_key_size;
|
||||
set_bit(HCI_CONN_STK_ENCRYPT, &hcon->flags);
|
||||
} else {
|
||||
@@ -1004,9 +1020,6 @@ static u8 smp_random(struct smp_chan *smp)
|
||||
|
||||
smp_s1(smp->tfm_aes, smp->tk, smp->prnd, smp->rrnd, stk);
|
||||
|
||||
memset(stk + smp->enc_key_size, 0,
|
||||
SMP_MAX_ENC_KEY_SIZE - smp->enc_key_size);
|
||||
|
||||
if (hcon->pending_sec_level == BT_SECURITY_HIGH)
|
||||
auth = 1;
|
||||
else
|
||||
@@ -1144,9 +1157,6 @@ static void sc_add_ltk(struct smp_chan *smp)
|
||||
else
|
||||
auth = 0;
|
||||
|
||||
memset(smp->tk + smp->enc_key_size, 0,
|
||||
SMP_MAX_ENC_KEY_SIZE - smp->enc_key_size);
|
||||
|
||||
smp->ltk = hci_add_ltk(hcon->hdev, &hcon->dst, hcon->dst_type,
|
||||
key_type, auth, smp->tk, smp->enc_key_size,
|
||||
0, 0);
|
||||
@@ -1268,7 +1278,14 @@ static void smp_distribute_keys(struct smp_chan *smp)
|
||||
__le16 ediv;
|
||||
__le64 rand;
|
||||
|
||||
get_random_bytes(enc.ltk, sizeof(enc.ltk));
|
||||
/* Make sure we generate only the significant amount of
|
||||
* bytes based on the encryption key size, and set the rest
|
||||
* of the value to zeroes.
|
||||
*/
|
||||
get_random_bytes(enc.ltk, smp->enc_key_size);
|
||||
memset(enc.ltk + smp->enc_key_size, 0,
|
||||
sizeof(enc.ltk) - smp->enc_key_size);
|
||||
|
||||
get_random_bytes(&ediv, sizeof(ediv));
|
||||
get_random_bytes(&rand, sizeof(rand));
|
||||
|
||||
@@ -1688,7 +1705,7 @@ static void build_bredr_pairing_cmd(struct smp_chan *smp,
|
||||
|
||||
req->init_key_dist = local_dist;
|
||||
req->resp_key_dist = remote_dist;
|
||||
req->max_key_size = SMP_MAX_ENC_KEY_SIZE;
|
||||
req->max_key_size = conn->hcon->enc_key_size;
|
||||
|
||||
smp->remote_key_dist = remote_dist;
|
||||
|
||||
@@ -1697,7 +1714,7 @@ static void build_bredr_pairing_cmd(struct smp_chan *smp,
|
||||
|
||||
memset(rsp, 0, sizeof(*rsp));
|
||||
|
||||
rsp->max_key_size = SMP_MAX_ENC_KEY_SIZE;
|
||||
rsp->max_key_size = conn->hcon->enc_key_size;
|
||||
rsp->init_key_dist = req->init_key_dist & remote_dist;
|
||||
rsp->resp_key_dist = req->resp_key_dist & local_dist;
|
||||
|
||||
@@ -2190,7 +2207,7 @@ static bool smp_ltk_encrypt(struct l2cap_conn *conn, u8 sec_level)
|
||||
if (test_and_set_bit(HCI_CONN_ENCRYPT_PEND, &hcon->flags))
|
||||
return true;
|
||||
|
||||
hci_le_start_enc(hcon, key->ediv, key->rand, key->val);
|
||||
hci_le_start_enc(hcon, key->ediv, key->rand, key->val, key->enc_size);
|
||||
hcon->enc_key_size = key->enc_size;
|
||||
|
||||
/* We never store STKs for master role, so clear this flag */
|
||||
@@ -2738,7 +2755,7 @@ static int smp_cmd_dhkey_check(struct l2cap_conn *conn, struct sk_buff *skb)
|
||||
sc_add_ltk(smp);
|
||||
|
||||
if (hcon->out) {
|
||||
hci_le_start_enc(hcon, 0, 0, smp->tk);
|
||||
hci_le_start_enc(hcon, 0, 0, smp->tk, smp->enc_key_size);
|
||||
hcon->enc_key_size = smp->enc_key_size;
|
||||
}
|
||||
|
||||
@@ -3120,6 +3137,8 @@ static struct l2cap_chan *smp_add_cid(struct hci_dev *hdev, u16 cid)
|
||||
|
||||
smp->tfm_aes = tfm_aes;
|
||||
smp->tfm_cmac = tfm_cmac;
|
||||
smp->min_key_size = SMP_MIN_ENC_KEY_SIZE;
|
||||
smp->max_key_size = SMP_MAX_ENC_KEY_SIZE;
|
||||
|
||||
create_chan:
|
||||
chan = l2cap_chan_create();
|
||||
@@ -3242,6 +3261,94 @@ static const struct file_operations force_bredr_smp_fops = {
|
||||
.llseek = default_llseek,
|
||||
};
|
||||
|
||||
static ssize_t le_min_key_size_read(struct file *file,
|
||||
char __user *user_buf,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
struct hci_dev *hdev = file->private_data;
|
||||
char buf[4];
|
||||
|
||||
snprintf(buf, sizeof(buf), "%2u\n", SMP_DEV(hdev)->min_key_size);
|
||||
|
||||
return simple_read_from_buffer(user_buf, count, ppos, buf, strlen(buf));
|
||||
}
|
||||
|
||||
static ssize_t le_min_key_size_write(struct file *file,
|
||||
const char __user *user_buf,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
struct hci_dev *hdev = file->private_data;
|
||||
char buf[32];
|
||||
size_t buf_size = min(count, (sizeof(buf) - 1));
|
||||
u8 key_size;
|
||||
|
||||
if (copy_from_user(buf, user_buf, buf_size))
|
||||
return -EFAULT;
|
||||
|
||||
buf[buf_size] = '\0';
|
||||
|
||||
sscanf(buf, "%hhu", &key_size);
|
||||
|
||||
if (key_size > SMP_DEV(hdev)->max_key_size ||
|
||||
key_size < SMP_MIN_ENC_KEY_SIZE)
|
||||
return -EINVAL;
|
||||
|
||||
SMP_DEV(hdev)->min_key_size = key_size;
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static const struct file_operations le_min_key_size_fops = {
|
||||
.open = simple_open,
|
||||
.read = le_min_key_size_read,
|
||||
.write = le_min_key_size_write,
|
||||
.llseek = default_llseek,
|
||||
};
|
||||
|
||||
static ssize_t le_max_key_size_read(struct file *file,
|
||||
char __user *user_buf,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
struct hci_dev *hdev = file->private_data;
|
||||
char buf[4];
|
||||
|
||||
snprintf(buf, sizeof(buf), "%2u\n", SMP_DEV(hdev)->max_key_size);
|
||||
|
||||
return simple_read_from_buffer(user_buf, count, ppos, buf, strlen(buf));
|
||||
}
|
||||
|
||||
static ssize_t le_max_key_size_write(struct file *file,
|
||||
const char __user *user_buf,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
struct hci_dev *hdev = file->private_data;
|
||||
char buf[32];
|
||||
size_t buf_size = min(count, (sizeof(buf) - 1));
|
||||
u8 key_size;
|
||||
|
||||
if (copy_from_user(buf, user_buf, buf_size))
|
||||
return -EFAULT;
|
||||
|
||||
buf[buf_size] = '\0';
|
||||
|
||||
sscanf(buf, "%hhu", &key_size);
|
||||
|
||||
if (key_size > SMP_MAX_ENC_KEY_SIZE ||
|
||||
key_size < SMP_DEV(hdev)->min_key_size)
|
||||
return -EINVAL;
|
||||
|
||||
SMP_DEV(hdev)->max_key_size = key_size;
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static const struct file_operations le_max_key_size_fops = {
|
||||
.open = simple_open,
|
||||
.read = le_max_key_size_read,
|
||||
.write = le_max_key_size_write,
|
||||
.llseek = default_llseek,
|
||||
};
|
||||
|
||||
int smp_register(struct hci_dev *hdev)
|
||||
{
|
||||
struct l2cap_chan *chan;
|
||||
@@ -3266,6 +3373,11 @@ int smp_register(struct hci_dev *hdev)
|
||||
|
||||
hdev->smp_data = chan;
|
||||
|
||||
debugfs_create_file("le_min_key_size", 0644, hdev->debugfs, hdev,
|
||||
&le_min_key_size_fops);
|
||||
debugfs_create_file("le_max_key_size", 0644, hdev->debugfs, hdev,
|
||||
&le_max_key_size_fops);
|
||||
|
||||
/* If the controller does not support BR/EDR Secure Connections
|
||||
* feature, then the BR/EDR SMP channel shall not be present.
|
||||
*
|
||||
|
||||
@@ -12,6 +12,8 @@ bridge-$(CONFIG_SYSFS) += br_sysfs_if.o br_sysfs_br.o
|
||||
|
||||
bridge-$(subst m,y,$(CONFIG_BRIDGE_NETFILTER)) += br_nf_core.o
|
||||
|
||||
br_netfilter-y := br_netfilter_hooks.o
|
||||
br_netfilter-$(subst m,y,$(CONFIG_IPV6)) += br_netfilter_ipv6.o
|
||||
obj-$(CONFIG_BRIDGE_NETFILTER) += br_netfilter.o
|
||||
|
||||
bridge-$(CONFIG_BRIDGE_IGMP_SNOOPING) += br_multicast.o br_mdb.o
|
||||
|
||||
@@ -121,13 +121,13 @@ static struct notifier_block br_device_notifier = {
|
||||
.notifier_call = br_device_event
|
||||
};
|
||||
|
||||
static int br_netdev_switch_event(struct notifier_block *unused,
|
||||
unsigned long event, void *ptr)
|
||||
static int br_switchdev_event(struct notifier_block *unused,
|
||||
unsigned long event, void *ptr)
|
||||
{
|
||||
struct net_device *dev = netdev_switch_notifier_info_to_dev(ptr);
|
||||
struct net_device *dev = switchdev_notifier_info_to_dev(ptr);
|
||||
struct net_bridge_port *p;
|
||||
struct net_bridge *br;
|
||||
struct netdev_switch_notifier_fdb_info *fdb_info;
|
||||
struct switchdev_notifier_fdb_info *fdb_info;
|
||||
int err = NOTIFY_DONE;
|
||||
|
||||
rtnl_lock();
|
||||
@@ -138,14 +138,14 @@ static int br_netdev_switch_event(struct notifier_block *unused,
|
||||
br = p->br;
|
||||
|
||||
switch (event) {
|
||||
case NETDEV_SWITCH_FDB_ADD:
|
||||
case SWITCHDEV_FDB_ADD:
|
||||
fdb_info = ptr;
|
||||
err = br_fdb_external_learn_add(br, p, fdb_info->addr,
|
||||
fdb_info->vid);
|
||||
if (err)
|
||||
err = notifier_from_errno(err);
|
||||
break;
|
||||
case NETDEV_SWITCH_FDB_DEL:
|
||||
case SWITCHDEV_FDB_DEL:
|
||||
fdb_info = ptr;
|
||||
err = br_fdb_external_learn_del(br, p, fdb_info->addr,
|
||||
fdb_info->vid);
|
||||
@@ -159,8 +159,8 @@ static int br_netdev_switch_event(struct notifier_block *unused,
|
||||
return err;
|
||||
}
|
||||
|
||||
static struct notifier_block br_netdev_switch_notifier = {
|
||||
.notifier_call = br_netdev_switch_event,
|
||||
static struct notifier_block br_switchdev_notifier = {
|
||||
.notifier_call = br_switchdev_event,
|
||||
};
|
||||
|
||||
static void __net_exit br_net_exit(struct net *net)
|
||||
@@ -214,7 +214,7 @@ static int __init br_init(void)
|
||||
if (err)
|
||||
goto err_out3;
|
||||
|
||||
err = register_netdev_switch_notifier(&br_netdev_switch_notifier);
|
||||
err = register_switchdev_notifier(&br_switchdev_notifier);
|
||||
if (err)
|
||||
goto err_out4;
|
||||
|
||||
@@ -235,7 +235,7 @@ static int __init br_init(void)
|
||||
return 0;
|
||||
|
||||
err_out5:
|
||||
unregister_netdev_switch_notifier(&br_netdev_switch_notifier);
|
||||
unregister_switchdev_notifier(&br_switchdev_notifier);
|
||||
err_out4:
|
||||
unregister_netdevice_notifier(&br_device_notifier);
|
||||
err_out3:
|
||||
@@ -253,7 +253,7 @@ static void __exit br_deinit(void)
|
||||
{
|
||||
stp_proto_unregister(&br_stp_proto);
|
||||
br_netlink_fini();
|
||||
unregister_netdev_switch_notifier(&br_netdev_switch_notifier);
|
||||
unregister_switchdev_notifier(&br_switchdev_notifier);
|
||||
unregister_netdevice_notifier(&br_device_notifier);
|
||||
brioctl_set(NULL);
|
||||
unregister_pernet_subsys(&br_net_ops);
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
#include <linux/atomic.h>
|
||||
#include <asm/unaligned.h>
|
||||
#include <linux/if_vlan.h>
|
||||
#include <net/switchdev.h>
|
||||
#include "br_private.h"
|
||||
|
||||
static struct kmem_cache *br_fdb_cache __read_mostly;
|
||||
@@ -130,11 +131,27 @@ static void fdb_del_hw_addr(struct net_bridge *br, const unsigned char *addr)
|
||||
}
|
||||
}
|
||||
|
||||
static void fdb_del_external_learn(struct net_bridge_fdb_entry *f)
|
||||
{
|
||||
struct switchdev_obj obj = {
|
||||
.id = SWITCHDEV_OBJ_PORT_FDB,
|
||||
.u.fdb = {
|
||||
.addr = f->addr.addr,
|
||||
.vid = f->vlan_id,
|
||||
},
|
||||
};
|
||||
|
||||
switchdev_port_obj_del(f->dst->dev, &obj);
|
||||
}
|
||||
|
||||
static void fdb_delete(struct net_bridge *br, struct net_bridge_fdb_entry *f)
|
||||
{
|
||||
if (f->is_static)
|
||||
fdb_del_hw_addr(br, f->addr.addr);
|
||||
|
||||
if (f->added_by_external_learn)
|
||||
fdb_del_external_learn(f);
|
||||
|
||||
hlist_del_rcu(&f->hlist);
|
||||
fdb_notify(br, f, RTM_DELNEIGH);
|
||||
call_rcu(&f->rcu, fdb_rcu_free);
|
||||
@@ -313,9 +330,11 @@ void br_fdb_flush(struct net_bridge *br)
|
||||
|
||||
/* Flush all entries referring to a specific port.
|
||||
* if do_all is set also flush static entries
|
||||
* if vid is set delete all entries that match the vlan_id
|
||||
*/
|
||||
void br_fdb_delete_by_port(struct net_bridge *br,
|
||||
const struct net_bridge_port *p,
|
||||
u16 vid,
|
||||
int do_all)
|
||||
{
|
||||
int i;
|
||||
@@ -330,8 +349,9 @@ void br_fdb_delete_by_port(struct net_bridge *br,
|
||||
if (f->dst != p)
|
||||
continue;
|
||||
|
||||
if (f->is_static && !do_all)
|
||||
continue;
|
||||
if (!do_all)
|
||||
if (f->is_static || (vid && f->vlan_id != vid))
|
||||
continue;
|
||||
|
||||
if (f->is_local)
|
||||
fdb_delete_local(br, p, f);
|
||||
@@ -736,6 +756,12 @@ static int fdb_add_entry(struct net_bridge_port *source, const __u8 *addr,
|
||||
struct net_bridge_fdb_entry *fdb;
|
||||
bool modified = false;
|
||||
|
||||
/* If the port cannot learn allow only local and static entries */
|
||||
if (!(state & NUD_PERMANENT) && !(state & NUD_NOARP) &&
|
||||
!(source->state == BR_STATE_LEARNING ||
|
||||
source->state == BR_STATE_FORWARDING))
|
||||
return -EPERM;
|
||||
|
||||
fdb = fdb_find(head, addr, vid);
|
||||
if (fdb == NULL) {
|
||||
if (!(flags & NLM_F_CREATE))
|
||||
@@ -867,13 +893,15 @@ int br_fdb_add(struct ndmsg *ndm, struct nlattr *tb[],
|
||||
return err;
|
||||
}
|
||||
|
||||
static int fdb_delete_by_addr(struct net_bridge *br, const u8 *addr, u16 vlan)
|
||||
static int fdb_delete_by_addr_and_port(struct net_bridge_port *p,
|
||||
const u8 *addr, u16 vlan)
|
||||
{
|
||||
struct net_bridge *br = p->br;
|
||||
struct hlist_head *head = &br->hash[br_mac_hash(addr, vlan)];
|
||||
struct net_bridge_fdb_entry *fdb;
|
||||
|
||||
fdb = fdb_find(head, addr, vlan);
|
||||
if (!fdb)
|
||||
if (!fdb || fdb->dst != p)
|
||||
return -ENOENT;
|
||||
|
||||
fdb_delete(br, fdb);
|
||||
@@ -886,7 +914,7 @@ static int __br_fdb_delete(struct net_bridge_port *p,
|
||||
int err;
|
||||
|
||||
spin_lock_bh(&p->br->hash_lock);
|
||||
err = fdb_delete_by_addr(p->br, addr, vid);
|
||||
err = fdb_delete_by_addr_and_port(p, addr, vid);
|
||||
spin_unlock_bh(&p->br->hash_lock);
|
||||
|
||||
return err;
|
||||
|
||||
@@ -249,7 +249,7 @@ static void del_nbp(struct net_bridge_port *p)
|
||||
list_del_rcu(&p->list);
|
||||
|
||||
nbp_vlan_flush(p);
|
||||
br_fdb_delete_by_port(br, p, 1);
|
||||
br_fdb_delete_by_port(br, p, 0, 1);
|
||||
nbp_update_port_count(br);
|
||||
|
||||
netdev_upper_dev_unlink(dev, br->dev);
|
||||
@@ -278,7 +278,7 @@ void br_dev_delete(struct net_device *dev, struct list_head *head)
|
||||
del_nbp(p);
|
||||
}
|
||||
|
||||
br_fdb_delete_by_port(br, NULL, 1);
|
||||
br_fdb_delete_by_port(br, NULL, 0, 1);
|
||||
|
||||
br_vlan_flush(br);
|
||||
del_timer_sync(&br->gc_timer);
|
||||
|
||||
@@ -247,9 +247,7 @@ static int old_dev_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
|
||||
if (!ns_capable(dev_net(dev)->user_ns, CAP_NET_ADMIN))
|
||||
return -EPERM;
|
||||
|
||||
spin_lock_bh(&br->lock);
|
||||
br_stp_set_bridge_priority(br, args[1]);
|
||||
spin_unlock_bh(&br->lock);
|
||||
return 0;
|
||||
|
||||
case BRCTL_SET_PORT_PRIORITY:
|
||||
|
||||
@@ -37,6 +37,8 @@
|
||||
|
||||
static void br_multicast_start_querier(struct net_bridge *br,
|
||||
struct bridge_mcast_own_query *query);
|
||||
static void br_multicast_add_router(struct net_bridge *br,
|
||||
struct net_bridge_port *port);
|
||||
unsigned int br_mdb_rehash_seq;
|
||||
|
||||
static inline int br_ip_equal(const struct br_ip *a, const struct br_ip *b)
|
||||
@@ -936,6 +938,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))
|
||||
br_multicast_add_router(br, port);
|
||||
|
||||
out:
|
||||
spin_unlock(&br->multicast_lock);
|
||||
@@ -975,9 +979,6 @@ static int br_ip4_multicast_igmp3_report(struct net_bridge *br,
|
||||
int err = 0;
|
||||
__be32 group;
|
||||
|
||||
if (!pskb_may_pull(skb, sizeof(*ih)))
|
||||
return -EINVAL;
|
||||
|
||||
ih = igmpv3_report_hdr(skb);
|
||||
num = ntohs(ih->ngrec);
|
||||
len = sizeof(*ih);
|
||||
@@ -1247,25 +1248,14 @@ static int br_ip4_multicast_query(struct net_bridge *br,
|
||||
max_delay = 10 * HZ;
|
||||
group = 0;
|
||||
}
|
||||
} else {
|
||||
if (!pskb_may_pull(skb, sizeof(struct igmpv3_query))) {
|
||||
err = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
} else if (skb->len >= sizeof(*ih3)) {
|
||||
ih3 = igmpv3_query_hdr(skb);
|
||||
if (ih3->nsrcs)
|
||||
goto out;
|
||||
|
||||
max_delay = ih3->code ?
|
||||
IGMPV3_MRC(ih3->code) * (HZ / IGMP_TIMER_SCALE) : 1;
|
||||
}
|
||||
|
||||
/* RFC2236+RFC3376 (IGMPv2+IGMPv3) require the multicast link layer
|
||||
* all-systems destination addresses (224.0.0.1) for general queries
|
||||
*/
|
||||
if (!group && iph->daddr != htonl(INADDR_ALLHOSTS_GROUP)) {
|
||||
err = -EINVAL;
|
||||
} else {
|
||||
goto out;
|
||||
}
|
||||
|
||||
@@ -1328,12 +1318,6 @@ static int br_ip6_multicast_query(struct net_bridge *br,
|
||||
(port && port->state == BR_STATE_DISABLED))
|
||||
goto out;
|
||||
|
||||
/* RFC2710+RFC3810 (MLDv1+MLDv2) require link-local source addresses */
|
||||
if (!(ipv6_addr_type(&ip6h->saddr) & IPV6_ADDR_LINKLOCAL)) {
|
||||
err = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (skb->len == sizeof(*mld)) {
|
||||
if (!pskb_may_pull(skb, sizeof(*mld))) {
|
||||
err = -EINVAL;
|
||||
@@ -1357,14 +1341,6 @@ static int br_ip6_multicast_query(struct net_bridge *br,
|
||||
|
||||
is_general_query = group && ipv6_addr_any(group);
|
||||
|
||||
/* RFC2710+RFC3810 (MLDv1+MLDv2) require the multicast link layer
|
||||
* all-nodes destination address (ff02::1) for general queries
|
||||
*/
|
||||
if (is_general_query && !ipv6_addr_is_ll_all_nodes(&ip6h->daddr)) {
|
||||
err = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (is_general_query) {
|
||||
saddr.proto = htons(ETH_P_IPV6);
|
||||
saddr.u.ip6 = ip6h->saddr;
|
||||
@@ -1556,74 +1532,22 @@ static int br_multicast_ipv4_rcv(struct net_bridge *br,
|
||||
struct sk_buff *skb,
|
||||
u16 vid)
|
||||
{
|
||||
struct sk_buff *skb2 = skb;
|
||||
const struct iphdr *iph;
|
||||
struct sk_buff *skb_trimmed = NULL;
|
||||
struct igmphdr *ih;
|
||||
unsigned int len;
|
||||
unsigned int offset;
|
||||
int err;
|
||||
|
||||
/* We treat OOM as packet loss for now. */
|
||||
if (!pskb_may_pull(skb, sizeof(*iph)))
|
||||
return -EINVAL;
|
||||
err = ip_mc_check_igmp(skb, &skb_trimmed);
|
||||
|
||||
iph = ip_hdr(skb);
|
||||
|
||||
if (iph->ihl < 5 || iph->version != 4)
|
||||
return -EINVAL;
|
||||
|
||||
if (!pskb_may_pull(skb, ip_hdrlen(skb)))
|
||||
return -EINVAL;
|
||||
|
||||
iph = ip_hdr(skb);
|
||||
|
||||
if (unlikely(ip_fast_csum((u8 *)iph, iph->ihl)))
|
||||
return -EINVAL;
|
||||
|
||||
if (iph->protocol != IPPROTO_IGMP) {
|
||||
if (!ipv4_is_local_multicast(iph->daddr))
|
||||
if (err == -ENOMSG) {
|
||||
if (!ipv4_is_local_multicast(ip_hdr(skb)->daddr))
|
||||
BR_INPUT_SKB_CB(skb)->mrouters_only = 1;
|
||||
return 0;
|
||||
} else if (err < 0) {
|
||||
return err;
|
||||
}
|
||||
|
||||
len = ntohs(iph->tot_len);
|
||||
if (skb->len < len || len < ip_hdrlen(skb))
|
||||
return -EINVAL;
|
||||
|
||||
if (skb->len > len) {
|
||||
skb2 = skb_clone(skb, GFP_ATOMIC);
|
||||
if (!skb2)
|
||||
return -ENOMEM;
|
||||
|
||||
err = pskb_trim_rcsum(skb2, len);
|
||||
if (err)
|
||||
goto err_out;
|
||||
}
|
||||
|
||||
len -= ip_hdrlen(skb2);
|
||||
offset = skb_network_offset(skb2) + ip_hdrlen(skb2);
|
||||
__skb_pull(skb2, offset);
|
||||
skb_reset_transport_header(skb2);
|
||||
|
||||
err = -EINVAL;
|
||||
if (!pskb_may_pull(skb2, sizeof(*ih)))
|
||||
goto out;
|
||||
|
||||
switch (skb2->ip_summed) {
|
||||
case CHECKSUM_COMPLETE:
|
||||
if (!csum_fold(skb2->csum))
|
||||
break;
|
||||
/* fall through */
|
||||
case CHECKSUM_NONE:
|
||||
skb2->csum = 0;
|
||||
if (skb_checksum_complete(skb2))
|
||||
goto out;
|
||||
}
|
||||
|
||||
err = 0;
|
||||
|
||||
BR_INPUT_SKB_CB(skb)->igmp = 1;
|
||||
ih = igmp_hdr(skb2);
|
||||
ih = igmp_hdr(skb);
|
||||
|
||||
switch (ih->type) {
|
||||
case IGMP_HOST_MEMBERSHIP_REPORT:
|
||||
@@ -1632,21 +1556,19 @@ static int br_multicast_ipv4_rcv(struct net_bridge *br,
|
||||
err = br_ip4_multicast_add_group(br, port, ih->group, vid);
|
||||
break;
|
||||
case IGMPV3_HOST_MEMBERSHIP_REPORT:
|
||||
err = br_ip4_multicast_igmp3_report(br, port, skb2, vid);
|
||||
err = br_ip4_multicast_igmp3_report(br, port, skb_trimmed, vid);
|
||||
break;
|
||||
case IGMP_HOST_MEMBERSHIP_QUERY:
|
||||
err = br_ip4_multicast_query(br, port, skb2, vid);
|
||||
err = br_ip4_multicast_query(br, port, skb_trimmed, vid);
|
||||
break;
|
||||
case IGMP_HOST_LEAVE_MESSAGE:
|
||||
br_ip4_multicast_leave_group(br, port, ih->group, vid);
|
||||
break;
|
||||
}
|
||||
|
||||
out:
|
||||
__skb_push(skb2, offset);
|
||||
err_out:
|
||||
if (skb2 != skb)
|
||||
kfree_skb(skb2);
|
||||
if (skb_trimmed)
|
||||
kfree_skb(skb_trimmed);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
@@ -1656,138 +1578,42 @@ static int br_multicast_ipv6_rcv(struct net_bridge *br,
|
||||
struct sk_buff *skb,
|
||||
u16 vid)
|
||||
{
|
||||
struct sk_buff *skb2;
|
||||
const struct ipv6hdr *ip6h;
|
||||
u8 icmp6_type;
|
||||
u8 nexthdr;
|
||||
__be16 frag_off;
|
||||
unsigned int len;
|
||||
int offset;
|
||||
struct sk_buff *skb_trimmed = NULL;
|
||||
struct mld_msg *mld;
|
||||
int err;
|
||||
|
||||
if (!pskb_may_pull(skb, sizeof(*ip6h)))
|
||||
return -EINVAL;
|
||||
err = ipv6_mc_check_mld(skb, &skb_trimmed);
|
||||
|
||||
ip6h = ipv6_hdr(skb);
|
||||
|
||||
/*
|
||||
* We're interested in MLD messages only.
|
||||
* - Version is 6
|
||||
* - MLD has always Router Alert hop-by-hop option
|
||||
* - But we do not support jumbrograms.
|
||||
*/
|
||||
if (ip6h->version != 6)
|
||||
if (err == -ENOMSG) {
|
||||
if (!ipv6_addr_is_ll_all_nodes(&ipv6_hdr(skb)->daddr))
|
||||
BR_INPUT_SKB_CB(skb)->mrouters_only = 1;
|
||||
return 0;
|
||||
|
||||
/* Prevent flooding this packet if there is no listener present */
|
||||
if (!ipv6_addr_is_ll_all_nodes(&ip6h->daddr))
|
||||
BR_INPUT_SKB_CB(skb)->mrouters_only = 1;
|
||||
|
||||
if (ip6h->nexthdr != IPPROTO_HOPOPTS ||
|
||||
ip6h->payload_len == 0)
|
||||
return 0;
|
||||
|
||||
len = ntohs(ip6h->payload_len) + sizeof(*ip6h);
|
||||
if (skb->len < len)
|
||||
return -EINVAL;
|
||||
|
||||
nexthdr = ip6h->nexthdr;
|
||||
offset = ipv6_skip_exthdr(skb, sizeof(*ip6h), &nexthdr, &frag_off);
|
||||
|
||||
if (offset < 0 || nexthdr != IPPROTO_ICMPV6)
|
||||
return 0;
|
||||
|
||||
/* Okay, we found ICMPv6 header */
|
||||
skb2 = skb_clone(skb, GFP_ATOMIC);
|
||||
if (!skb2)
|
||||
return -ENOMEM;
|
||||
|
||||
err = -EINVAL;
|
||||
if (!pskb_may_pull(skb2, offset + sizeof(struct icmp6hdr)))
|
||||
goto out;
|
||||
|
||||
len -= offset - skb_network_offset(skb2);
|
||||
|
||||
__skb_pull(skb2, offset);
|
||||
skb_reset_transport_header(skb2);
|
||||
skb_postpull_rcsum(skb2, skb_network_header(skb2),
|
||||
skb_network_header_len(skb2));
|
||||
|
||||
icmp6_type = icmp6_hdr(skb2)->icmp6_type;
|
||||
|
||||
switch (icmp6_type) {
|
||||
case ICMPV6_MGM_QUERY:
|
||||
case ICMPV6_MGM_REPORT:
|
||||
case ICMPV6_MGM_REDUCTION:
|
||||
case ICMPV6_MLD2_REPORT:
|
||||
break;
|
||||
default:
|
||||
err = 0;
|
||||
goto out;
|
||||
} else if (err < 0) {
|
||||
return err;
|
||||
}
|
||||
|
||||
/* Okay, we found MLD message. Check further. */
|
||||
if (skb2->len > len) {
|
||||
err = pskb_trim_rcsum(skb2, len);
|
||||
if (err)
|
||||
goto out;
|
||||
err = -EINVAL;
|
||||
}
|
||||
|
||||
ip6h = ipv6_hdr(skb2);
|
||||
|
||||
switch (skb2->ip_summed) {
|
||||
case CHECKSUM_COMPLETE:
|
||||
if (!csum_ipv6_magic(&ip6h->saddr, &ip6h->daddr, skb2->len,
|
||||
IPPROTO_ICMPV6, skb2->csum))
|
||||
break;
|
||||
/*FALLTHROUGH*/
|
||||
case CHECKSUM_NONE:
|
||||
skb2->csum = ~csum_unfold(csum_ipv6_magic(&ip6h->saddr,
|
||||
&ip6h->daddr,
|
||||
skb2->len,
|
||||
IPPROTO_ICMPV6, 0));
|
||||
if (__skb_checksum_complete(skb2))
|
||||
goto out;
|
||||
}
|
||||
|
||||
err = 0;
|
||||
|
||||
BR_INPUT_SKB_CB(skb)->igmp = 1;
|
||||
mld = (struct mld_msg *)skb_transport_header(skb);
|
||||
|
||||
switch (icmp6_type) {
|
||||
switch (mld->mld_type) {
|
||||
case ICMPV6_MGM_REPORT:
|
||||
{
|
||||
struct mld_msg *mld;
|
||||
if (!pskb_may_pull(skb2, sizeof(*mld))) {
|
||||
err = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
mld = (struct mld_msg *)skb_transport_header(skb2);
|
||||
BR_INPUT_SKB_CB(skb)->mrouters_only = 1;
|
||||
err = br_ip6_multicast_add_group(br, port, &mld->mld_mca, vid);
|
||||
break;
|
||||
}
|
||||
case ICMPV6_MLD2_REPORT:
|
||||
err = br_ip6_multicast_mld2_report(br, port, skb2, vid);
|
||||
err = br_ip6_multicast_mld2_report(br, port, skb_trimmed, vid);
|
||||
break;
|
||||
case ICMPV6_MGM_QUERY:
|
||||
err = br_ip6_multicast_query(br, port, skb2, vid);
|
||||
err = br_ip6_multicast_query(br, port, skb_trimmed, vid);
|
||||
break;
|
||||
case ICMPV6_MGM_REDUCTION:
|
||||
{
|
||||
struct mld_msg *mld;
|
||||
if (!pskb_may_pull(skb2, sizeof(*mld))) {
|
||||
err = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
mld = (struct mld_msg *)skb_transport_header(skb2);
|
||||
br_ip6_multicast_leave_group(br, port, &mld->mld_mca, vid);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
out:
|
||||
kfree_skb(skb2);
|
||||
if (skb_trimmed)
|
||||
kfree_skb(skb_trimmed);
|
||||
|
||||
return err;
|
||||
}
|
||||
#endif
|
||||
@@ -1949,11 +1775,9 @@ void br_multicast_stop(struct net_bridge *br)
|
||||
|
||||
int br_multicast_set_router(struct net_bridge *br, unsigned long val)
|
||||
{
|
||||
int err = -ENOENT;
|
||||
int err = -EINVAL;
|
||||
|
||||
spin_lock_bh(&br->multicast_lock);
|
||||
if (!netif_running(br->dev))
|
||||
goto unlock;
|
||||
|
||||
switch (val) {
|
||||
case 0:
|
||||
@@ -1964,13 +1788,8 @@ int br_multicast_set_router(struct net_bridge *br, unsigned long val)
|
||||
br->multicast_router = val;
|
||||
err = 0;
|
||||
break;
|
||||
|
||||
default:
|
||||
err = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
unlock:
|
||||
spin_unlock_bh(&br->multicast_lock);
|
||||
|
||||
return err;
|
||||
@@ -1979,11 +1798,9 @@ int br_multicast_set_router(struct net_bridge *br, unsigned long val)
|
||||
int br_multicast_set_port_router(struct net_bridge_port *p, unsigned long val)
|
||||
{
|
||||
struct net_bridge *br = p->br;
|
||||
int err = -ENOENT;
|
||||
int err = -EINVAL;
|
||||
|
||||
spin_lock(&br->multicast_lock);
|
||||
if (!netif_running(br->dev) || p->state == BR_STATE_DISABLED)
|
||||
goto unlock;
|
||||
|
||||
switch (val) {
|
||||
case 0:
|
||||
@@ -2005,13 +1822,8 @@ int br_multicast_set_port_router(struct net_bridge_port *p, unsigned long val)
|
||||
|
||||
br_multicast_add_router(br, p);
|
||||
break;
|
||||
|
||||
default:
|
||||
err = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
unlock:
|
||||
spin_unlock(&br->multicast_lock);
|
||||
|
||||
return err;
|
||||
@@ -2116,15 +1928,11 @@ int br_multicast_set_querier(struct net_bridge *br, unsigned long val)
|
||||
|
||||
int br_multicast_set_hash_max(struct net_bridge *br, unsigned long val)
|
||||
{
|
||||
int err = -ENOENT;
|
||||
int err = -EINVAL;
|
||||
u32 old;
|
||||
struct net_bridge_mdb_htable *mdb;
|
||||
|
||||
spin_lock_bh(&br->multicast_lock);
|
||||
if (!netif_running(br->dev))
|
||||
goto unlock;
|
||||
|
||||
err = -EINVAL;
|
||||
if (!is_power_of_2(val))
|
||||
goto unlock;
|
||||
|
||||
|
||||
@@ -34,6 +34,7 @@
|
||||
|
||||
#include <net/ip.h>
|
||||
#include <net/ipv6.h>
|
||||
#include <net/addrconf.h>
|
||||
#include <net/route.h>
|
||||
#include <net/netfilter/br_netfilter.h>
|
||||
|
||||
@@ -115,22 +116,19 @@ struct brnf_frag_data {
|
||||
char mac[NF_BRIDGE_MAX_MAC_HEADER_LENGTH];
|
||||
u8 encap_size;
|
||||
u8 size;
|
||||
u16 vlan_tci;
|
||||
__be16 vlan_proto;
|
||||
};
|
||||
|
||||
static DEFINE_PER_CPU(struct brnf_frag_data, brnf_frag_data_storage);
|
||||
#endif
|
||||
|
||||
static struct nf_bridge_info *nf_bridge_info_get(const struct sk_buff *skb)
|
||||
static void nf_bridge_info_free(struct sk_buff *skb)
|
||||
{
|
||||
return skb->nf_bridge;
|
||||
}
|
||||
|
||||
static inline struct rtable *bridge_parent_rtable(const struct net_device *dev)
|
||||
{
|
||||
struct net_bridge_port *port;
|
||||
|
||||
port = br_port_get_rcu(dev);
|
||||
return port ? &port->br->fake_rtable : NULL;
|
||||
if (skb->nf_bridge) {
|
||||
nf_bridge_put(skb->nf_bridge);
|
||||
skb->nf_bridge = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static inline struct net_device *bridge_parent(const struct net_device *dev)
|
||||
@@ -141,15 +139,6 @@ static inline struct net_device *bridge_parent(const struct net_device *dev)
|
||||
return port ? port->br->dev : NULL;
|
||||
}
|
||||
|
||||
static inline struct nf_bridge_info *nf_bridge_alloc(struct sk_buff *skb)
|
||||
{
|
||||
skb->nf_bridge = kzalloc(sizeof(struct nf_bridge_info), GFP_ATOMIC);
|
||||
if (likely(skb->nf_bridge))
|
||||
atomic_set(&(skb->nf_bridge->use), 1);
|
||||
|
||||
return skb->nf_bridge;
|
||||
}
|
||||
|
||||
static inline struct nf_bridge_info *nf_bridge_unshare(struct sk_buff *skb)
|
||||
{
|
||||
struct nf_bridge_info *nf_bridge = skb->nf_bridge;
|
||||
@@ -167,7 +156,7 @@ static inline struct nf_bridge_info *nf_bridge_unshare(struct sk_buff *skb)
|
||||
return nf_bridge;
|
||||
}
|
||||
|
||||
static unsigned int nf_bridge_encap_header_len(const struct sk_buff *skb)
|
||||
unsigned int nf_bridge_encap_header_len(const struct sk_buff *skb)
|
||||
{
|
||||
switch (skb->protocol) {
|
||||
case __cpu_to_be16(ETH_P_8021Q):
|
||||
@@ -179,14 +168,6 @@ static unsigned int nf_bridge_encap_header_len(const struct sk_buff *skb)
|
||||
}
|
||||
}
|
||||
|
||||
static inline void nf_bridge_push_encap_header(struct sk_buff *skb)
|
||||
{
|
||||
unsigned int len = nf_bridge_encap_header_len(skb);
|
||||
|
||||
skb_push(skb, len);
|
||||
skb->network_header -= len;
|
||||
}
|
||||
|
||||
static inline void nf_bridge_pull_encap_header(struct sk_buff *skb)
|
||||
{
|
||||
unsigned int len = nf_bridge_encap_header_len(skb);
|
||||
@@ -208,7 +189,7 @@ static inline void nf_bridge_pull_encap_header_rcsum(struct sk_buff *skb)
|
||||
* expected format
|
||||
*/
|
||||
|
||||
static int br_parse_ip_options(struct sk_buff *skb)
|
||||
static int br_validate_ipv4(struct sk_buff *skb)
|
||||
{
|
||||
const struct iphdr *iph;
|
||||
struct net_device *dev = skb->dev;
|
||||
@@ -256,7 +237,7 @@ static int br_parse_ip_options(struct sk_buff *skb)
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void nf_bridge_update_protocol(struct sk_buff *skb)
|
||||
void nf_bridge_update_protocol(struct sk_buff *skb)
|
||||
{
|
||||
switch (skb->nf_bridge->orig_proto) {
|
||||
case BRNF_PROTO_8021Q:
|
||||
@@ -270,43 +251,12 @@ static void nf_bridge_update_protocol(struct sk_buff *skb)
|
||||
}
|
||||
}
|
||||
|
||||
/* PF_BRIDGE/PRE_ROUTING *********************************************/
|
||||
/* Undo the changes made for ip6tables PREROUTING and continue the
|
||||
* bridge PRE_ROUTING hook. */
|
||||
static int br_nf_pre_routing_finish_ipv6(struct sock *sk, struct sk_buff *skb)
|
||||
{
|
||||
struct nf_bridge_info *nf_bridge = nf_bridge_info_get(skb);
|
||||
struct rtable *rt;
|
||||
|
||||
if (nf_bridge->pkt_otherhost) {
|
||||
skb->pkt_type = PACKET_OTHERHOST;
|
||||
nf_bridge->pkt_otherhost = false;
|
||||
}
|
||||
nf_bridge->mask ^= BRNF_NF_BRIDGE_PREROUTING;
|
||||
|
||||
rt = bridge_parent_rtable(nf_bridge->physindev);
|
||||
if (!rt) {
|
||||
kfree_skb(skb);
|
||||
return 0;
|
||||
}
|
||||
skb_dst_set_noref(skb, &rt->dst);
|
||||
|
||||
skb->dev = nf_bridge->physindev;
|
||||
nf_bridge_update_protocol(skb);
|
||||
nf_bridge_push_encap_header(skb);
|
||||
NF_HOOK_THRESH(NFPROTO_BRIDGE, NF_BR_PRE_ROUTING, sk, skb,
|
||||
skb->dev, NULL,
|
||||
br_handle_frame_finish, 1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Obtain the correct destination MAC address, while preserving the original
|
||||
* source MAC address. If we already know this address, we just copy it. If we
|
||||
* don't, we use the neighbour framework to find out. In both cases, we make
|
||||
* sure that br_handle_frame_finish() is called afterwards.
|
||||
*/
|
||||
static int br_nf_pre_routing_finish_bridge(struct sock *sk, struct sk_buff *skb)
|
||||
int br_nf_pre_routing_finish_bridge(struct sock *sk, struct sk_buff *skb)
|
||||
{
|
||||
struct neighbour *neigh;
|
||||
struct dst_entry *dst;
|
||||
@@ -346,8 +296,9 @@ static int br_nf_pre_routing_finish_bridge(struct sock *sk, struct sk_buff *skb)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool daddr_was_changed(const struct sk_buff *skb,
|
||||
const struct nf_bridge_info *nf_bridge)
|
||||
static inline bool
|
||||
br_nf_ipv4_daddr_was_changed(const struct sk_buff *skb,
|
||||
const struct nf_bridge_info *nf_bridge)
|
||||
{
|
||||
return ip_hdr(skb)->daddr != nf_bridge->ipv4_daddr;
|
||||
}
|
||||
@@ -398,17 +349,15 @@ static int br_nf_pre_routing_finish(struct sock *sk, struct sk_buff *skb)
|
||||
struct nf_bridge_info *nf_bridge = nf_bridge_info_get(skb);
|
||||
struct rtable *rt;
|
||||
int err;
|
||||
int frag_max_size;
|
||||
|
||||
frag_max_size = IPCB(skb)->frag_max_size;
|
||||
BR_INPUT_SKB_CB(skb)->frag_max_size = frag_max_size;
|
||||
nf_bridge->frag_max_size = IPCB(skb)->frag_max_size;
|
||||
|
||||
if (nf_bridge->pkt_otherhost) {
|
||||
skb->pkt_type = PACKET_OTHERHOST;
|
||||
nf_bridge->pkt_otherhost = false;
|
||||
}
|
||||
nf_bridge->mask ^= BRNF_NF_BRIDGE_PREROUTING;
|
||||
if (daddr_was_changed(skb, nf_bridge)) {
|
||||
nf_bridge->mask &= ~BRNF_NF_BRIDGE_PREROUTING;
|
||||
if (br_nf_ipv4_daddr_was_changed(skb, nf_bridge)) {
|
||||
if ((err = ip_route_input(skb, iph->daddr, iph->saddr, iph->tos, dev))) {
|
||||
struct in_device *in_dev = __in_dev_get_rcu(dev);
|
||||
|
||||
@@ -486,7 +435,7 @@ static struct net_device *brnf_get_logical_dev(struct sk_buff *skb, const struct
|
||||
}
|
||||
|
||||
/* Some common code for IPv4/IPv6 */
|
||||
static struct net_device *setup_pre_routing(struct sk_buff *skb)
|
||||
struct net_device *setup_pre_routing(struct sk_buff *skb)
|
||||
{
|
||||
struct nf_bridge_info *nf_bridge = nf_bridge_info_get(skb);
|
||||
|
||||
@@ -509,106 +458,6 @@ static struct net_device *setup_pre_routing(struct sk_buff *skb)
|
||||
return skb->dev;
|
||||
}
|
||||
|
||||
/* We only check the length. A bridge shouldn't do any hop-by-hop stuff anyway */
|
||||
static int check_hbh_len(struct sk_buff *skb)
|
||||
{
|
||||
unsigned char *raw = (u8 *)(ipv6_hdr(skb) + 1);
|
||||
u32 pkt_len;
|
||||
const unsigned char *nh = skb_network_header(skb);
|
||||
int off = raw - nh;
|
||||
int len = (raw[1] + 1) << 3;
|
||||
|
||||
if ((raw + len) - skb->data > skb_headlen(skb))
|
||||
goto bad;
|
||||
|
||||
off += 2;
|
||||
len -= 2;
|
||||
|
||||
while (len > 0) {
|
||||
int optlen = nh[off + 1] + 2;
|
||||
|
||||
switch (nh[off]) {
|
||||
case IPV6_TLV_PAD1:
|
||||
optlen = 1;
|
||||
break;
|
||||
|
||||
case IPV6_TLV_PADN:
|
||||
break;
|
||||
|
||||
case IPV6_TLV_JUMBO:
|
||||
if (nh[off + 1] != 4 || (off & 3) != 2)
|
||||
goto bad;
|
||||
pkt_len = ntohl(*(__be32 *) (nh + off + 2));
|
||||
if (pkt_len <= IPV6_MAXPLEN ||
|
||||
ipv6_hdr(skb)->payload_len)
|
||||
goto bad;
|
||||
if (pkt_len > skb->len - sizeof(struct ipv6hdr))
|
||||
goto bad;
|
||||
if (pskb_trim_rcsum(skb,
|
||||
pkt_len + sizeof(struct ipv6hdr)))
|
||||
goto bad;
|
||||
nh = skb_network_header(skb);
|
||||
break;
|
||||
default:
|
||||
if (optlen > len)
|
||||
goto bad;
|
||||
break;
|
||||
}
|
||||
off += optlen;
|
||||
len -= optlen;
|
||||
}
|
||||
if (len == 0)
|
||||
return 0;
|
||||
bad:
|
||||
return -1;
|
||||
|
||||
}
|
||||
|
||||
/* Replicate the checks that IPv6 does on packet reception and pass the packet
|
||||
* to ip6tables, which doesn't support NAT, so things are fairly simple. */
|
||||
static unsigned int br_nf_pre_routing_ipv6(const struct nf_hook_ops *ops,
|
||||
struct sk_buff *skb,
|
||||
const struct nf_hook_state *state)
|
||||
{
|
||||
const struct ipv6hdr *hdr;
|
||||
u32 pkt_len;
|
||||
|
||||
if (skb->len < sizeof(struct ipv6hdr))
|
||||
return NF_DROP;
|
||||
|
||||
if (!pskb_may_pull(skb, sizeof(struct ipv6hdr)))
|
||||
return NF_DROP;
|
||||
|
||||
hdr = ipv6_hdr(skb);
|
||||
|
||||
if (hdr->version != 6)
|
||||
return NF_DROP;
|
||||
|
||||
pkt_len = ntohs(hdr->payload_len);
|
||||
|
||||
if (pkt_len || hdr->nexthdr != NEXTHDR_HOP) {
|
||||
if (pkt_len + sizeof(struct ipv6hdr) > skb->len)
|
||||
return NF_DROP;
|
||||
if (pskb_trim_rcsum(skb, pkt_len + sizeof(struct ipv6hdr)))
|
||||
return NF_DROP;
|
||||
}
|
||||
if (hdr->nexthdr == NEXTHDR_HOP && check_hbh_len(skb))
|
||||
return NF_DROP;
|
||||
|
||||
nf_bridge_put(skb->nf_bridge);
|
||||
if (!nf_bridge_alloc(skb))
|
||||
return NF_DROP;
|
||||
if (!setup_pre_routing(skb))
|
||||
return NF_DROP;
|
||||
|
||||
skb->protocol = htons(ETH_P_IPV6);
|
||||
NF_HOOK(NFPROTO_IPV6, NF_INET_PRE_ROUTING, state->sk, skb,
|
||||
skb->dev, NULL,
|
||||
br_nf_pre_routing_finish_ipv6);
|
||||
|
||||
return NF_STOLEN;
|
||||
}
|
||||
|
||||
/* Direct IPv6 traffic to br_nf_pre_routing_ipv6.
|
||||
* Replicate the checks that IPv4 does on packet reception.
|
||||
* Set skb->dev to the bridge device (i.e. parent of the
|
||||
@@ -648,7 +497,7 @@ static unsigned int br_nf_pre_routing(const struct nf_hook_ops *ops,
|
||||
|
||||
nf_bridge_pull_encap_header_rcsum(skb);
|
||||
|
||||
if (br_parse_ip_options(skb))
|
||||
if (br_validate_ipv4(skb))
|
||||
return NF_DROP;
|
||||
|
||||
nf_bridge_put(skb->nf_bridge);
|
||||
@@ -692,12 +541,12 @@ static int br_nf_forward_finish(struct sock *sk, struct sk_buff *skb)
|
||||
struct net_device *in;
|
||||
|
||||
if (!IS_ARP(skb) && !IS_VLAN_ARP(skb)) {
|
||||
int frag_max_size;
|
||||
|
||||
if (skb->protocol == htons(ETH_P_IP)) {
|
||||
frag_max_size = IPCB(skb)->frag_max_size;
|
||||
BR_INPUT_SKB_CB(skb)->frag_max_size = frag_max_size;
|
||||
}
|
||||
if (skb->protocol == htons(ETH_P_IP))
|
||||
nf_bridge->frag_max_size = IPCB(skb)->frag_max_size;
|
||||
|
||||
if (skb->protocol == htons(ETH_P_IPV6))
|
||||
nf_bridge->frag_max_size = IP6CB(skb)->frag_max_size;
|
||||
|
||||
in = nf_bridge->physindev;
|
||||
if (nf_bridge->pkt_otherhost) {
|
||||
@@ -760,12 +609,15 @@ static unsigned int br_nf_forward_ip(const struct nf_hook_ops *ops,
|
||||
}
|
||||
|
||||
if (pf == NFPROTO_IPV4) {
|
||||
int frag_max = BR_INPUT_SKB_CB(skb)->frag_max_size;
|
||||
|
||||
if (br_parse_ip_options(skb))
|
||||
if (br_validate_ipv4(skb))
|
||||
return NF_DROP;
|
||||
IPCB(skb)->frag_max_size = nf_bridge->frag_max_size;
|
||||
}
|
||||
|
||||
IPCB(skb)->frag_max_size = frag_max;
|
||||
if (pf == NFPROTO_IPV6) {
|
||||
if (br_validate_ipv6(skb))
|
||||
return NF_DROP;
|
||||
IP6CB(skb)->frag_max_size = nf_bridge->frag_max_size;
|
||||
}
|
||||
|
||||
nf_bridge->physoutdev = skb->dev;
|
||||
@@ -815,7 +667,7 @@ static unsigned int br_nf_forward_arp(const struct nf_hook_ops *ops,
|
||||
return NF_STOLEN;
|
||||
}
|
||||
|
||||
#if IS_ENABLED(CONFIG_NF_DEFRAG_IPV4)
|
||||
#if IS_ENABLED(CONFIG_NF_DEFRAG_IPV4) || IS_ENABLED(CONFIG_NF_DEFRAG_IPV6)
|
||||
static int br_nf_push_frag_xmit(struct sock *sk, struct sk_buff *skb)
|
||||
{
|
||||
struct brnf_frag_data *data;
|
||||
@@ -829,33 +681,95 @@ static int br_nf_push_frag_xmit(struct sock *sk, struct sk_buff *skb)
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (data->vlan_tci) {
|
||||
skb->vlan_tci = data->vlan_tci;
|
||||
skb->vlan_proto = data->vlan_proto;
|
||||
}
|
||||
|
||||
skb_copy_to_linear_data_offset(skb, -data->size, data->mac, data->size);
|
||||
__skb_push(skb, data->encap_size);
|
||||
|
||||
nf_bridge_info_free(skb);
|
||||
return br_dev_queue_push_xmit(sk, skb);
|
||||
}
|
||||
#endif
|
||||
|
||||
static int br_nf_ip_fragment(struct sock *sk, struct sk_buff *skb,
|
||||
int (*output)(struct sock *, struct sk_buff *))
|
||||
{
|
||||
unsigned int mtu = ip_skb_dst_mtu(skb);
|
||||
struct iphdr *iph = ip_hdr(skb);
|
||||
struct rtable *rt = skb_rtable(skb);
|
||||
struct net_device *dev = rt->dst.dev;
|
||||
|
||||
if (unlikely(((iph->frag_off & htons(IP_DF)) && !skb->ignore_df) ||
|
||||
(IPCB(skb)->frag_max_size &&
|
||||
IPCB(skb)->frag_max_size > mtu))) {
|
||||
IP_INC_STATS(dev_net(dev), IPSTATS_MIB_FRAGFAILS);
|
||||
kfree_skb(skb);
|
||||
return -EMSGSIZE;
|
||||
}
|
||||
|
||||
return ip_do_fragment(sk, skb, output);
|
||||
}
|
||||
|
||||
static unsigned int nf_bridge_mtu_reduction(const struct sk_buff *skb)
|
||||
{
|
||||
if (skb->nf_bridge->orig_proto == BRNF_PROTO_PPPOE)
|
||||
return PPPOE_SES_HLEN;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int br_nf_dev_queue_xmit(struct sock *sk, struct sk_buff *skb)
|
||||
{
|
||||
int ret;
|
||||
int frag_max_size;
|
||||
struct nf_bridge_info *nf_bridge;
|
||||
unsigned int mtu_reserved;
|
||||
|
||||
if (skb_is_gso(skb) || skb->protocol != htons(ETH_P_IP))
|
||||
return br_dev_queue_push_xmit(sk, skb);
|
||||
|
||||
mtu_reserved = nf_bridge_mtu_reduction(skb);
|
||||
|
||||
if (skb_is_gso(skb) || skb->len + mtu_reserved <= skb->dev->mtu) {
|
||||
nf_bridge_info_free(skb);
|
||||
return br_dev_queue_push_xmit(sk, skb);
|
||||
}
|
||||
|
||||
nf_bridge = nf_bridge_info_get(skb);
|
||||
|
||||
#if IS_ENABLED(CONFIG_NF_DEFRAG_IPV4)
|
||||
/* This is wrong! We should preserve the original fragment
|
||||
* boundaries by preserving frag_list rather than refragmenting.
|
||||
*/
|
||||
if (skb->len + mtu_reserved > skb->dev->mtu) {
|
||||
if (skb->protocol == htons(ETH_P_IP)) {
|
||||
struct brnf_frag_data *data;
|
||||
|
||||
frag_max_size = BR_INPUT_SKB_CB(skb)->frag_max_size;
|
||||
if (br_parse_ip_options(skb))
|
||||
/* Drop invalid packet */
|
||||
if (br_validate_ipv4(skb))
|
||||
return NF_DROP;
|
||||
IPCB(skb)->frag_max_size = frag_max_size;
|
||||
|
||||
IPCB(skb)->frag_max_size = nf_bridge->frag_max_size;
|
||||
|
||||
nf_bridge_update_protocol(skb);
|
||||
|
||||
data = this_cpu_ptr(&brnf_frag_data_storage);
|
||||
|
||||
data->vlan_tci = skb->vlan_tci;
|
||||
data->vlan_proto = skb->vlan_proto;
|
||||
data->encap_size = nf_bridge_encap_header_len(skb);
|
||||
data->size = ETH_HLEN + data->encap_size;
|
||||
|
||||
skb_copy_from_linear_data_offset(skb, -data->size, data->mac,
|
||||
data->size);
|
||||
|
||||
return br_nf_ip_fragment(sk, skb, br_nf_push_frag_xmit);
|
||||
}
|
||||
#endif
|
||||
#if IS_ENABLED(CONFIG_NF_DEFRAG_IPV6)
|
||||
if (skb->protocol == htons(ETH_P_IPV6)) {
|
||||
const struct nf_ipv6_ops *v6ops = nf_get_ipv6_ops();
|
||||
struct brnf_frag_data *data;
|
||||
|
||||
if (br_validate_ipv6(skb))
|
||||
return NF_DROP;
|
||||
|
||||
IP6CB(skb)->frag_max_size = nf_bridge->frag_max_size;
|
||||
|
||||
nf_bridge_update_protocol(skb);
|
||||
|
||||
@@ -866,19 +780,15 @@ static int br_nf_dev_queue_xmit(struct sock *sk, struct sk_buff *skb)
|
||||
skb_copy_from_linear_data_offset(skb, -data->size, data->mac,
|
||||
data->size);
|
||||
|
||||
ret = ip_fragment(sk, skb, br_nf_push_frag_xmit);
|
||||
} else {
|
||||
ret = br_dev_queue_push_xmit(sk, skb);
|
||||
if (v6ops)
|
||||
return v6ops->fragment(sk, skb, br_nf_push_frag_xmit);
|
||||
else
|
||||
return -EMSGSIZE;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
#else
|
||||
static int br_nf_dev_queue_xmit(struct sock *sk, struct sk_buff *skb)
|
||||
{
|
||||
return br_dev_queue_push_xmit(sk, skb);
|
||||
}
|
||||
#endif
|
||||
nf_bridge_info_free(skb);
|
||||
return br_dev_queue_push_xmit(sk, skb);
|
||||
}
|
||||
|
||||
/* PF_BRIDGE/POST_ROUTING ********************************************/
|
||||
static unsigned int br_nf_post_routing(const struct nf_hook_ops *ops,
|
||||
@@ -964,6 +874,8 @@ static void br_nf_pre_routing_finish_bridge_slow(struct sk_buff *skb)
|
||||
nf_bridge->neigh_header,
|
||||
ETH_HLEN - ETH_ALEN);
|
||||
skb->dev = nf_bridge->physindev;
|
||||
|
||||
nf_bridge->physoutdev = NULL;
|
||||
br_handle_frame_finish(NULL, skb);
|
||||
}
|
||||
|
||||
245
net/bridge/br_netfilter_ipv6.c
Normal file
245
net/bridge/br_netfilter_ipv6.c
Normal file
@@ -0,0 +1,245 @@
|
||||
/*
|
||||
* Handle firewalling
|
||||
* Linux ethernet bridge
|
||||
*
|
||||
* Authors:
|
||||
* Lennert Buytenhek <buytenh@gnu.org>
|
||||
* Bart De Schuymer <bdschuym@pandora.be>
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* Lennert dedicates this file to Kerstin Wurdinger.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/ip.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <linux/if_arp.h>
|
||||
#include <linux/if_ether.h>
|
||||
#include <linux/if_vlan.h>
|
||||
#include <linux/if_pppox.h>
|
||||
#include <linux/ppp_defs.h>
|
||||
#include <linux/netfilter_bridge.h>
|
||||
#include <linux/netfilter_ipv4.h>
|
||||
#include <linux/netfilter_ipv6.h>
|
||||
#include <linux/netfilter_arp.h>
|
||||
#include <linux/in_route.h>
|
||||
#include <linux/inetdevice.h>
|
||||
|
||||
#include <net/ip.h>
|
||||
#include <net/ipv6.h>
|
||||
#include <net/addrconf.h>
|
||||
#include <net/route.h>
|
||||
#include <net/netfilter/br_netfilter.h>
|
||||
|
||||
#include <asm/uaccess.h>
|
||||
#include "br_private.h"
|
||||
#ifdef CONFIG_SYSCTL
|
||||
#include <linux/sysctl.h>
|
||||
#endif
|
||||
|
||||
/* We only check the length. A bridge shouldn't do any hop-by-hop stuff
|
||||
* anyway
|
||||
*/
|
||||
static int br_nf_check_hbh_len(struct sk_buff *skb)
|
||||
{
|
||||
unsigned char *raw = (u8 *)(ipv6_hdr(skb) + 1);
|
||||
u32 pkt_len;
|
||||
const unsigned char *nh = skb_network_header(skb);
|
||||
int off = raw - nh;
|
||||
int len = (raw[1] + 1) << 3;
|
||||
|
||||
if ((raw + len) - skb->data > skb_headlen(skb))
|
||||
goto bad;
|
||||
|
||||
off += 2;
|
||||
len -= 2;
|
||||
|
||||
while (len > 0) {
|
||||
int optlen = nh[off + 1] + 2;
|
||||
|
||||
switch (nh[off]) {
|
||||
case IPV6_TLV_PAD1:
|
||||
optlen = 1;
|
||||
break;
|
||||
|
||||
case IPV6_TLV_PADN:
|
||||
break;
|
||||
|
||||
case IPV6_TLV_JUMBO:
|
||||
if (nh[off + 1] != 4 || (off & 3) != 2)
|
||||
goto bad;
|
||||
pkt_len = ntohl(*(__be32 *)(nh + off + 2));
|
||||
if (pkt_len <= IPV6_MAXPLEN ||
|
||||
ipv6_hdr(skb)->payload_len)
|
||||
goto bad;
|
||||
if (pkt_len > skb->len - sizeof(struct ipv6hdr))
|
||||
goto bad;
|
||||
if (pskb_trim_rcsum(skb,
|
||||
pkt_len + sizeof(struct ipv6hdr)))
|
||||
goto bad;
|
||||
nh = skb_network_header(skb);
|
||||
break;
|
||||
default:
|
||||
if (optlen > len)
|
||||
goto bad;
|
||||
break;
|
||||
}
|
||||
off += optlen;
|
||||
len -= optlen;
|
||||
}
|
||||
if (len == 0)
|
||||
return 0;
|
||||
bad:
|
||||
return -1;
|
||||
}
|
||||
|
||||
int br_validate_ipv6(struct sk_buff *skb)
|
||||
{
|
||||
const struct ipv6hdr *hdr;
|
||||
struct net_device *dev = skb->dev;
|
||||
struct inet6_dev *idev = in6_dev_get(skb->dev);
|
||||
u32 pkt_len;
|
||||
u8 ip6h_len = sizeof(struct ipv6hdr);
|
||||
|
||||
if (!pskb_may_pull(skb, ip6h_len))
|
||||
goto inhdr_error;
|
||||
|
||||
if (skb->len < ip6h_len)
|
||||
goto drop;
|
||||
|
||||
hdr = ipv6_hdr(skb);
|
||||
|
||||
if (hdr->version != 6)
|
||||
goto inhdr_error;
|
||||
|
||||
pkt_len = ntohs(hdr->payload_len);
|
||||
|
||||
if (pkt_len || hdr->nexthdr != NEXTHDR_HOP) {
|
||||
if (pkt_len + ip6h_len > skb->len) {
|
||||
IP6_INC_STATS_BH(dev_net(dev), idev,
|
||||
IPSTATS_MIB_INTRUNCATEDPKTS);
|
||||
goto drop;
|
||||
}
|
||||
if (pskb_trim_rcsum(skb, pkt_len + ip6h_len)) {
|
||||
IP6_INC_STATS_BH(dev_net(dev), idev,
|
||||
IPSTATS_MIB_INDISCARDS);
|
||||
goto drop;
|
||||
}
|
||||
}
|
||||
if (hdr->nexthdr == NEXTHDR_HOP && br_nf_check_hbh_len(skb))
|
||||
goto drop;
|
||||
|
||||
memset(IP6CB(skb), 0, sizeof(struct inet6_skb_parm));
|
||||
/* No IP options in IPv6 header; however it should be
|
||||
* checked if some next headers need special treatment
|
||||
*/
|
||||
return 0;
|
||||
|
||||
inhdr_error:
|
||||
IP6_INC_STATS_BH(dev_net(dev), idev, IPSTATS_MIB_INHDRERRORS);
|
||||
drop:
|
||||
return -1;
|
||||
}
|
||||
|
||||
static inline bool
|
||||
br_nf_ipv6_daddr_was_changed(const struct sk_buff *skb,
|
||||
const struct nf_bridge_info *nf_bridge)
|
||||
{
|
||||
return memcmp(&nf_bridge->ipv6_daddr, &ipv6_hdr(skb)->daddr,
|
||||
sizeof(ipv6_hdr(skb)->daddr)) != 0;
|
||||
}
|
||||
|
||||
/* PF_BRIDGE/PRE_ROUTING: Undo the changes made for ip6tables
|
||||
* PREROUTING and continue the bridge PRE_ROUTING hook. See comment
|
||||
* for br_nf_pre_routing_finish(), same logic is used here but
|
||||
* equivalent IPv6 function ip6_route_input() called indirectly.
|
||||
*/
|
||||
static int br_nf_pre_routing_finish_ipv6(struct sock *sk, struct sk_buff *skb)
|
||||
{
|
||||
struct nf_bridge_info *nf_bridge = nf_bridge_info_get(skb);
|
||||
struct rtable *rt;
|
||||
struct net_device *dev = skb->dev;
|
||||
const struct nf_ipv6_ops *v6ops = nf_get_ipv6_ops();
|
||||
|
||||
nf_bridge->frag_max_size = IP6CB(skb)->frag_max_size;
|
||||
|
||||
if (nf_bridge->pkt_otherhost) {
|
||||
skb->pkt_type = PACKET_OTHERHOST;
|
||||
nf_bridge->pkt_otherhost = false;
|
||||
}
|
||||
nf_bridge->mask &= ~BRNF_NF_BRIDGE_PREROUTING;
|
||||
if (br_nf_ipv6_daddr_was_changed(skb, nf_bridge)) {
|
||||
skb_dst_drop(skb);
|
||||
v6ops->route_input(skb);
|
||||
|
||||
if (skb_dst(skb)->error) {
|
||||
kfree_skb(skb);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (skb_dst(skb)->dev == dev) {
|
||||
skb->dev = nf_bridge->physindev;
|
||||
nf_bridge_update_protocol(skb);
|
||||
nf_bridge_push_encap_header(skb);
|
||||
NF_HOOK_THRESH(NFPROTO_BRIDGE, NF_BR_PRE_ROUTING,
|
||||
sk, skb, skb->dev, NULL,
|
||||
br_nf_pre_routing_finish_bridge,
|
||||
1);
|
||||
return 0;
|
||||
}
|
||||
ether_addr_copy(eth_hdr(skb)->h_dest, dev->dev_addr);
|
||||
skb->pkt_type = PACKET_HOST;
|
||||
} else {
|
||||
rt = bridge_parent_rtable(nf_bridge->physindev);
|
||||
if (!rt) {
|
||||
kfree_skb(skb);
|
||||
return 0;
|
||||
}
|
||||
skb_dst_set_noref(skb, &rt->dst);
|
||||
}
|
||||
|
||||
skb->dev = nf_bridge->physindev;
|
||||
nf_bridge_update_protocol(skb);
|
||||
nf_bridge_push_encap_header(skb);
|
||||
NF_HOOK_THRESH(NFPROTO_BRIDGE, NF_BR_PRE_ROUTING, sk, skb,
|
||||
skb->dev, NULL,
|
||||
br_handle_frame_finish, 1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Replicate the checks that IPv6 does on packet reception and pass the packet
|
||||
* to ip6tables.
|
||||
*/
|
||||
unsigned int br_nf_pre_routing_ipv6(const struct nf_hook_ops *ops,
|
||||
struct sk_buff *skb,
|
||||
const struct nf_hook_state *state)
|
||||
{
|
||||
struct nf_bridge_info *nf_bridge;
|
||||
|
||||
if (br_validate_ipv6(skb))
|
||||
return NF_DROP;
|
||||
|
||||
nf_bridge_put(skb->nf_bridge);
|
||||
if (!nf_bridge_alloc(skb))
|
||||
return NF_DROP;
|
||||
if (!setup_pre_routing(skb))
|
||||
return NF_DROP;
|
||||
|
||||
nf_bridge = nf_bridge_info_get(skb);
|
||||
nf_bridge->ipv6_daddr = ipv6_hdr(skb)->daddr;
|
||||
|
||||
skb->protocol = htons(ETH_P_IPV6);
|
||||
NF_HOOK(NFPROTO_IPV6, NF_INET_PRE_ROUTING, state->sk, skb,
|
||||
skb->dev, NULL,
|
||||
br_nf_pre_routing_finish_ipv6);
|
||||
|
||||
return NF_STOLEN;
|
||||
}
|
||||
@@ -586,7 +586,7 @@ int br_setlink(struct net_device *dev, struct nlmsghdr *nlh, u16 flags)
|
||||
struct nlattr *afspec;
|
||||
struct net_bridge_port *p;
|
||||
struct nlattr *tb[IFLA_BRPORT_MAX + 1];
|
||||
int err = 0, ret_offload = 0;
|
||||
int err = 0;
|
||||
|
||||
protinfo = nlmsg_find_attr(nlh, sizeof(struct ifinfomsg), IFLA_PROTINFO);
|
||||
afspec = nlmsg_find_attr(nlh, sizeof(struct ifinfomsg), IFLA_AF_SPEC);
|
||||
@@ -628,16 +628,6 @@ int br_setlink(struct net_device *dev, struct nlmsghdr *nlh, u16 flags)
|
||||
afspec, RTM_SETLINK);
|
||||
}
|
||||
|
||||
if (p && !(flags & BRIDGE_FLAGS_SELF)) {
|
||||
/* set bridge attributes in hardware if supported
|
||||
*/
|
||||
ret_offload = netdev_switch_port_bridge_setlink(dev, nlh,
|
||||
flags);
|
||||
if (ret_offload && ret_offload != -EOPNOTSUPP)
|
||||
br_warn(p->br, "error setting attrs on port %u(%s)\n",
|
||||
(unsigned int)p->port_no, p->dev->name);
|
||||
}
|
||||
|
||||
if (err == 0)
|
||||
br_ifinfo_notify(RTM_NEWLINK, p);
|
||||
out:
|
||||
@@ -649,7 +639,7 @@ int br_dellink(struct net_device *dev, struct nlmsghdr *nlh, u16 flags)
|
||||
{
|
||||
struct nlattr *afspec;
|
||||
struct net_bridge_port *p;
|
||||
int err = 0, ret_offload = 0;
|
||||
int err = 0;
|
||||
|
||||
afspec = nlmsg_find_attr(nlh, sizeof(struct ifinfomsg), IFLA_AF_SPEC);
|
||||
if (!afspec)
|
||||
@@ -668,16 +658,6 @@ int br_dellink(struct net_device *dev, struct nlmsghdr *nlh, u16 flags)
|
||||
*/
|
||||
br_ifinfo_notify(RTM_NEWLINK, p);
|
||||
|
||||
if (p && !(flags & BRIDGE_FLAGS_SELF)) {
|
||||
/* del bridge attributes in hardware
|
||||
*/
|
||||
ret_offload = netdev_switch_port_bridge_dellink(dev, nlh,
|
||||
flags);
|
||||
if (ret_offload && ret_offload != -EOPNOTSUPP)
|
||||
br_warn(p->br, "error deleting attrs on port %u (%s)\n",
|
||||
(unsigned int)p->port_no, p->dev->name);
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
static int br_validate(struct nlattr *tb[], struct nlattr *data[])
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
#include <linux/netpoll.h>
|
||||
#include <linux/u64_stats_sync.h>
|
||||
#include <net/route.h>
|
||||
#include <net/ip6_fib.h>
|
||||
#include <linux/if_vlan.h>
|
||||
|
||||
#define BR_HASH_BITS 8
|
||||
@@ -33,8 +34,8 @@
|
||||
|
||||
/* Control of forwarding link local multicast */
|
||||
#define BR_GROUPFWD_DEFAULT 0
|
||||
/* Don't allow forwarding control protocols like STP and LLDP */
|
||||
#define BR_GROUPFWD_RESTRICTED 0x4007u
|
||||
/* Don't allow forwarding of control protocols like STP, MAC PAUSE and LACP */
|
||||
#define BR_GROUPFWD_RESTRICTED 0x0007u
|
||||
/* The Nearest Customer Bridge Group Address, 01-80-C2-00-00-[00,0B,0C,0D,0F] */
|
||||
#define BR_GROUPFWD_8021AD 0xB801u
|
||||
|
||||
@@ -214,7 +215,10 @@ struct net_bridge
|
||||
spinlock_t hash_lock;
|
||||
struct hlist_head hash[BR_HASH_SIZE];
|
||||
#if IS_ENABLED(CONFIG_BRIDGE_NETFILTER)
|
||||
struct rtable fake_rtable;
|
||||
union {
|
||||
struct rtable fake_rtable;
|
||||
struct rt6_info fake_rt6_info;
|
||||
};
|
||||
bool nf_call_iptables;
|
||||
bool nf_call_ip6tables;
|
||||
bool nf_call_arptables;
|
||||
@@ -304,7 +308,6 @@ struct br_input_skb_cb {
|
||||
int mrouters_only;
|
||||
#endif
|
||||
|
||||
u16 frag_max_size;
|
||||
bool proxyarp_replied;
|
||||
|
||||
#ifdef CONFIG_BRIDGE_VLAN_FILTERING
|
||||
@@ -384,7 +387,7 @@ void br_fdb_changeaddr(struct net_bridge_port *p, const unsigned char *newaddr);
|
||||
void br_fdb_change_mac_address(struct net_bridge *br, const u8 *newaddr);
|
||||
void br_fdb_cleanup(unsigned long arg);
|
||||
void br_fdb_delete_by_port(struct net_bridge *br,
|
||||
const struct net_bridge_port *p, int do_all);
|
||||
const struct net_bridge_port *p, u16 vid, int do_all);
|
||||
struct net_bridge_fdb_entry *__br_fdb_get(struct net_bridge *br,
|
||||
const unsigned char *addr, __u16 vid);
|
||||
int br_fdb_test_addr(struct net_device *dev, unsigned char *addr);
|
||||
|
||||
@@ -39,10 +39,14 @@ void br_log_state(const struct net_bridge_port *p)
|
||||
|
||||
void br_set_state(struct net_bridge_port *p, unsigned int state)
|
||||
{
|
||||
struct switchdev_attr attr = {
|
||||
.id = SWITCHDEV_ATTR_PORT_STP_STATE,
|
||||
.u.stp_state = state,
|
||||
};
|
||||
int err;
|
||||
|
||||
p->state = state;
|
||||
err = netdev_switch_port_stp_update(p->dev, state);
|
||||
err = switchdev_port_attr_set(p->dev, &attr);
|
||||
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);
|
||||
@@ -424,7 +428,6 @@ static void br_make_forwarding(struct net_bridge_port *p)
|
||||
else
|
||||
br_set_state(p, BR_STATE_LEARNING);
|
||||
|
||||
br_multicast_enable_port(p);
|
||||
br_log_state(p);
|
||||
br_ifinfo_notify(RTM_NEWLINK, p);
|
||||
|
||||
@@ -458,6 +461,12 @@ void br_port_state_selection(struct net_bridge *br)
|
||||
}
|
||||
}
|
||||
|
||||
if (p->state != BR_STATE_BLOCKING)
|
||||
br_multicast_enable_port(p);
|
||||
/* Multicast is not disabled for the port when it goes in
|
||||
* blocking state because the timers will expire and stop by
|
||||
* themselves without sending more queries.
|
||||
*/
|
||||
if (p->state == BR_STATE_FORWARDING)
|
||||
++liveports;
|
||||
}
|
||||
|
||||
@@ -111,7 +111,7 @@ void br_stp_disable_port(struct net_bridge_port *p)
|
||||
del_timer(&p->forward_delay_timer);
|
||||
del_timer(&p->hold_timer);
|
||||
|
||||
br_fdb_delete_by_port(br, p, 0);
|
||||
br_fdb_delete_by_port(br, p, 0, 0);
|
||||
br_multicast_disable_port(p);
|
||||
|
||||
br_configuration_update(br);
|
||||
@@ -243,12 +243,13 @@ bool br_stp_recalculate_bridge_id(struct net_bridge *br)
|
||||
return true;
|
||||
}
|
||||
|
||||
/* called under bridge lock */
|
||||
/* Acquires and releases bridge lock */
|
||||
void br_stp_set_bridge_priority(struct net_bridge *br, u16 newprio)
|
||||
{
|
||||
struct net_bridge_port *p;
|
||||
int wasroot;
|
||||
|
||||
spin_lock_bh(&br->lock);
|
||||
wasroot = br_is_root_bridge(br);
|
||||
|
||||
list_for_each_entry(p, &br->port_list, list) {
|
||||
@@ -266,6 +267,7 @@ void br_stp_set_bridge_priority(struct net_bridge *br, u16 newprio)
|
||||
br_port_state_selection(br);
|
||||
if (br_is_root_bridge(br) && !wasroot)
|
||||
br_become_root_bridge(br);
|
||||
spin_unlock_bh(&br->lock);
|
||||
}
|
||||
|
||||
/* called under bridge lock */
|
||||
|
||||
@@ -160,7 +160,7 @@ static BRPORT_ATTR(hold_timer, S_IRUGO, show_hold_timer, NULL);
|
||||
|
||||
static int store_flush(struct net_bridge_port *p, unsigned long v)
|
||||
{
|
||||
br_fdb_delete_by_port(p->br, p, 0); // Don't delete local entry
|
||||
br_fdb_delete_by_port(p->br, p, 0, 0); // Don't delete local entry
|
||||
return 0;
|
||||
}
|
||||
static BRPORT_ATTR(flush, S_IWUSR, NULL, store_flush);
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/rtnetlink.h>
|
||||
#include <linux/slab.h>
|
||||
#include <net/switchdev.h>
|
||||
|
||||
#include "br_private.h"
|
||||
|
||||
@@ -36,6 +37,36 @@ static void __vlan_add_flags(struct net_port_vlans *v, u16 vid, u16 flags)
|
||||
clear_bit(vid, v->untagged_bitmap);
|
||||
}
|
||||
|
||||
static int __vlan_vid_add(struct net_device *dev, struct net_bridge *br,
|
||||
u16 vid, u16 flags)
|
||||
{
|
||||
const struct net_device_ops *ops = dev->netdev_ops;
|
||||
int err;
|
||||
|
||||
/* If driver uses VLAN ndo ops, use 8021q to install vid
|
||||
* on device, otherwise try switchdev ops to install vid.
|
||||
*/
|
||||
|
||||
if (ops->ndo_vlan_rx_add_vid) {
|
||||
err = vlan_vid_add(dev, br->vlan_proto, vid);
|
||||
} else {
|
||||
struct switchdev_obj vlan_obj = {
|
||||
.id = SWITCHDEV_OBJ_PORT_VLAN,
|
||||
.u.vlan = {
|
||||
.flags = flags,
|
||||
.vid_begin = vid,
|
||||
.vid_end = vid,
|
||||
},
|
||||
};
|
||||
|
||||
err = switchdev_port_obj_add(dev, &vlan_obj);
|
||||
if (err == -EOPNOTSUPP)
|
||||
err = 0;
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int __vlan_add(struct net_port_vlans *v, u16 vid, u16 flags)
|
||||
{
|
||||
struct net_bridge_port *p = NULL;
|
||||
@@ -62,7 +93,7 @@ static int __vlan_add(struct net_port_vlans *v, u16 vid, u16 flags)
|
||||
* This ensures tagged traffic enters the bridge when
|
||||
* promiscuous mode is disabled by br_manage_promisc().
|
||||
*/
|
||||
err = vlan_vid_add(dev, br->vlan_proto, vid);
|
||||
err = __vlan_vid_add(dev, br, vid, flags);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
@@ -86,6 +117,30 @@ static int __vlan_add(struct net_port_vlans *v, u16 vid, u16 flags)
|
||||
return err;
|
||||
}
|
||||
|
||||
static void __vlan_vid_del(struct net_device *dev, struct net_bridge *br,
|
||||
u16 vid)
|
||||
{
|
||||
const struct net_device_ops *ops = dev->netdev_ops;
|
||||
|
||||
/* If driver uses VLAN ndo ops, use 8021q to delete vid
|
||||
* on device, otherwise try switchdev ops to delete vid.
|
||||
*/
|
||||
|
||||
if (ops->ndo_vlan_rx_kill_vid) {
|
||||
vlan_vid_del(dev, br->vlan_proto, vid);
|
||||
} else {
|
||||
struct switchdev_obj vlan_obj = {
|
||||
.id = SWITCHDEV_OBJ_PORT_VLAN,
|
||||
.u.vlan = {
|
||||
.vid_begin = vid,
|
||||
.vid_end = vid,
|
||||
},
|
||||
};
|
||||
|
||||
switchdev_port_obj_del(dev, &vlan_obj);
|
||||
}
|
||||
}
|
||||
|
||||
static int __vlan_del(struct net_port_vlans *v, u16 vid)
|
||||
{
|
||||
if (!test_bit(vid, v->vlan_bitmap))
|
||||
@@ -96,7 +151,7 @@ static int __vlan_del(struct net_port_vlans *v, u16 vid)
|
||||
|
||||
if (v->port_idx) {
|
||||
struct net_bridge_port *p = v->parent.port;
|
||||
vlan_vid_del(p->dev, p->br->vlan_proto, vid);
|
||||
__vlan_vid_del(p->dev, p->br, vid);
|
||||
}
|
||||
|
||||
clear_bit(vid, v->vlan_bitmap);
|
||||
@@ -686,6 +741,7 @@ int nbp_vlan_delete(struct net_bridge_port *port, u16 vid)
|
||||
return -EINVAL;
|
||||
|
||||
br_fdb_find_delete_local(port->br, port, port->dev->dev_addr, vid);
|
||||
br_fdb_delete_by_port(port->br, port, vid, 0);
|
||||
|
||||
return __vlan_del(pv, vid);
|
||||
}
|
||||
|
||||
@@ -164,8 +164,10 @@ static int ebt_stp_mt_check(const struct xt_mtchk_param *par)
|
||||
!(info->bitmask & EBT_STP_MASK))
|
||||
return -EINVAL;
|
||||
/* Make sure the match only receives stp frames */
|
||||
if (!ether_addr_equal(e->destmac, bridge_ula) ||
|
||||
!ether_addr_equal(e->destmsk, msk) || !(e->bitmask & EBT_DESTMAC))
|
||||
if (!par->nft_compat &&
|
||||
(!ether_addr_equal(e->destmac, bridge_ula) ||
|
||||
!ether_addr_equal(e->destmsk, msk) ||
|
||||
!(e->bitmask & EBT_DESTMAC)))
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
|
||||
@@ -139,7 +139,7 @@ ebt_basic_match(const struct ebt_entry *e, const struct sk_buff *skb,
|
||||
ethproto = h->h_proto;
|
||||
|
||||
if (e->bitmask & EBT_802_3) {
|
||||
if (FWINV2(ntohs(ethproto) >= ETH_P_802_3_MIN, EBT_IPROTO))
|
||||
if (FWINV2(eth_proto_is_802_3(ethproto), EBT_IPROTO))
|
||||
return 1;
|
||||
} else if (!(e->bitmask & EBT_NOPROTO) &&
|
||||
FWINV2(e->ethproto != ethproto, EBT_IPROTO))
|
||||
|
||||
@@ -1055,7 +1055,7 @@ static int caif_create(struct net *net, struct socket *sock, int protocol,
|
||||
* is really not used at all in the net/core or socket.c but the
|
||||
* initialization makes sure that sock->state is not uninitialized.
|
||||
*/
|
||||
sk = sk_alloc(net, PF_CAIF, GFP_KERNEL, &prot);
|
||||
sk = sk_alloc(net, PF_CAIF, GFP_KERNEL, &prot, kern);
|
||||
if (!sk)
|
||||
return -ENOMEM;
|
||||
|
||||
|
||||
@@ -179,7 +179,7 @@ static int can_create(struct net *net, struct socket *sock, int protocol,
|
||||
|
||||
sock->ops = cp->ops;
|
||||
|
||||
sk = sk_alloc(net, PF_CAN, GFP_KERNEL, cp->prot);
|
||||
sk = sk_alloc(net, PF_CAN, GFP_KERNEL, cp->prot, kern);
|
||||
if (!sk) {
|
||||
err = -ENOMEM;
|
||||
goto errout;
|
||||
@@ -310,8 +310,12 @@ int can_send(struct sk_buff *skb, int loop)
|
||||
return err;
|
||||
}
|
||||
|
||||
if (newskb)
|
||||
if (newskb) {
|
||||
if (!(newskb->tstamp.tv64))
|
||||
__net_timestamp(newskb);
|
||||
|
||||
netif_rx_ni(newskb);
|
||||
}
|
||||
|
||||
/* update statistics */
|
||||
can_stats.tx_frames++;
|
||||
|
||||
68
net/can/gw.c
68
net/can/gw.c
@@ -110,6 +110,7 @@ struct cf_mod {
|
||||
void (*xor)(struct can_frame *cf, struct cgw_csum_xor *xor);
|
||||
void (*crc8)(struct can_frame *cf, struct cgw_csum_crc8 *crc8);
|
||||
} csumfunc;
|
||||
u32 uid;
|
||||
};
|
||||
|
||||
|
||||
@@ -548,6 +549,11 @@ static int cgw_put_job(struct sk_buff *skb, struct cgw_job *gwj, int type,
|
||||
goto cancel;
|
||||
}
|
||||
|
||||
if (gwj->mod.uid) {
|
||||
if (nla_put_u32(skb, CGW_MOD_UID, gwj->mod.uid) < 0)
|
||||
goto cancel;
|
||||
}
|
||||
|
||||
if (gwj->mod.csumfunc.crc8) {
|
||||
if (nla_put(skb, CGW_CS_CRC8, CGW_CS_CRC8_LEN,
|
||||
&gwj->mod.csum.crc8) < 0)
|
||||
@@ -619,6 +625,7 @@ static const struct nla_policy cgw_policy[CGW_MAX+1] = {
|
||||
[CGW_DST_IF] = { .type = NLA_U32 },
|
||||
[CGW_FILTER] = { .len = sizeof(struct can_filter) },
|
||||
[CGW_LIM_HOPS] = { .type = NLA_U8 },
|
||||
[CGW_MOD_UID] = { .type = NLA_U32 },
|
||||
};
|
||||
|
||||
/* check for common and gwtype specific attributes */
|
||||
@@ -761,6 +768,10 @@ static int cgw_parse_attr(struct nlmsghdr *nlh, struct cf_mod *mod,
|
||||
else
|
||||
mod->csumfunc.xor = cgw_csum_xor_neg;
|
||||
}
|
||||
|
||||
if (tb[CGW_MOD_UID]) {
|
||||
nla_memcpy(&mod->uid, tb[CGW_MOD_UID], sizeof(u32));
|
||||
}
|
||||
}
|
||||
|
||||
if (gwtype == CGW_TYPE_CAN_CAN) {
|
||||
@@ -802,6 +813,8 @@ static int cgw_create_job(struct sk_buff *skb, struct nlmsghdr *nlh)
|
||||
{
|
||||
struct rtcanmsg *r;
|
||||
struct cgw_job *gwj;
|
||||
struct cf_mod mod;
|
||||
struct can_can_gw ccgw;
|
||||
u8 limhops = 0;
|
||||
int err = 0;
|
||||
|
||||
@@ -819,6 +832,36 @@ static int cgw_create_job(struct sk_buff *skb, struct nlmsghdr *nlh)
|
||||
if (r->gwtype != CGW_TYPE_CAN_CAN)
|
||||
return -EINVAL;
|
||||
|
||||
err = cgw_parse_attr(nlh, &mod, CGW_TYPE_CAN_CAN, &ccgw, &limhops);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
if (mod.uid) {
|
||||
|
||||
ASSERT_RTNL();
|
||||
|
||||
/* check for updating an existing job with identical uid */
|
||||
hlist_for_each_entry(gwj, &cgw_list, list) {
|
||||
|
||||
if (gwj->mod.uid != mod.uid)
|
||||
continue;
|
||||
|
||||
/* interfaces & filters must be identical */
|
||||
if (memcmp(&gwj->ccgw, &ccgw, sizeof(ccgw)))
|
||||
return -EINVAL;
|
||||
|
||||
/* update modifications with disabled softirq & quit */
|
||||
local_bh_disable();
|
||||
memcpy(&gwj->mod, &mod, sizeof(mod));
|
||||
local_bh_enable();
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* ifindex == 0 is not allowed for job creation */
|
||||
if (!ccgw.src_idx || !ccgw.dst_idx)
|
||||
return -ENODEV;
|
||||
|
||||
gwj = kmem_cache_alloc(cgw_cache, GFP_KERNEL);
|
||||
if (!gwj)
|
||||
return -ENOMEM;
|
||||
@@ -828,18 +871,14 @@ static int cgw_create_job(struct sk_buff *skb, struct nlmsghdr *nlh)
|
||||
gwj->deleted_frames = 0;
|
||||
gwj->flags = r->flags;
|
||||
gwj->gwtype = r->gwtype;
|
||||
gwj->limit_hops = limhops;
|
||||
|
||||
err = cgw_parse_attr(nlh, &gwj->mod, CGW_TYPE_CAN_CAN, &gwj->ccgw,
|
||||
&limhops);
|
||||
if (err < 0)
|
||||
goto out;
|
||||
/* insert already parsed information */
|
||||
memcpy(&gwj->mod, &mod, sizeof(mod));
|
||||
memcpy(&gwj->ccgw, &ccgw, sizeof(ccgw));
|
||||
|
||||
err = -ENODEV;
|
||||
|
||||
/* ifindex == 0 is not allowed for job creation */
|
||||
if (!gwj->ccgw.src_idx || !gwj->ccgw.dst_idx)
|
||||
goto out;
|
||||
|
||||
gwj->src.dev = __dev_get_by_index(&init_net, gwj->ccgw.src_idx);
|
||||
|
||||
if (!gwj->src.dev)
|
||||
@@ -856,8 +895,6 @@ static int cgw_create_job(struct sk_buff *skb, struct nlmsghdr *nlh)
|
||||
if (gwj->dst.dev->type != ARPHRD_CAN)
|
||||
goto out;
|
||||
|
||||
gwj->limit_hops = limhops;
|
||||
|
||||
ASSERT_RTNL();
|
||||
|
||||
err = cgw_register_filter(gwj);
|
||||
@@ -931,8 +968,15 @@ static int cgw_remove_job(struct sk_buff *skb, struct nlmsghdr *nlh)
|
||||
if (gwj->limit_hops != limhops)
|
||||
continue;
|
||||
|
||||
if (memcmp(&gwj->mod, &mod, sizeof(mod)))
|
||||
continue;
|
||||
/* we have a match when uid is enabled and identical */
|
||||
if (gwj->mod.uid || mod.uid) {
|
||||
if (gwj->mod.uid != mod.uid)
|
||||
continue;
|
||||
} else {
|
||||
/* no uid => check for identical modifications */
|
||||
if (memcmp(&gwj->mod, &mod, sizeof(mod)))
|
||||
continue;
|
||||
}
|
||||
|
||||
/* if (r->gwtype == CGW_TYPE_CAN_CAN) - is made sure here */
|
||||
if (memcmp(&gwj->ccgw, &ccgw, sizeof(ccgw)))
|
||||
|
||||
@@ -480,8 +480,8 @@ static int ceph_tcp_connect(struct ceph_connection *con)
|
||||
int ret;
|
||||
|
||||
BUG_ON(con->sock);
|
||||
ret = sock_create_kern(con->peer_addr.in_addr.ss_family, SOCK_STREAM,
|
||||
IPPROTO_TCP, &sock);
|
||||
ret = sock_create_kern(&init_net, con->peer_addr.in_addr.ss_family,
|
||||
SOCK_STREAM, IPPROTO_TCP, &sock);
|
||||
if (ret)
|
||||
return ret;
|
||||
sock->sk->sk_allocation = GFP_NOFS;
|
||||
|
||||
220
net/core/dev.c
220
net/core/dev.c
@@ -135,6 +135,7 @@
|
||||
#include <linux/if_macvlan.h>
|
||||
#include <linux/errqueue.h>
|
||||
#include <linux/hrtimer.h>
|
||||
#include <linux/netfilter_ingress.h>
|
||||
|
||||
#include "net-sysfs.h"
|
||||
|
||||
@@ -468,10 +469,14 @@ EXPORT_SYMBOL(dev_remove_pack);
|
||||
*/
|
||||
void dev_add_offload(struct packet_offload *po)
|
||||
{
|
||||
struct list_head *head = &offload_base;
|
||||
struct packet_offload *elem;
|
||||
|
||||
spin_lock(&offload_lock);
|
||||
list_add_rcu(&po->list, head);
|
||||
list_for_each_entry(elem, &offload_base, list) {
|
||||
if (po->priority < elem->priority)
|
||||
break;
|
||||
}
|
||||
list_add_rcu(&po->list, elem->list.prev);
|
||||
spin_unlock(&offload_lock);
|
||||
}
|
||||
EXPORT_SYMBOL(dev_add_offload);
|
||||
@@ -1630,7 +1635,7 @@ int call_netdevice_notifiers(unsigned long val, struct net_device *dev)
|
||||
}
|
||||
EXPORT_SYMBOL(call_netdevice_notifiers);
|
||||
|
||||
#ifdef CONFIG_NET_CLS_ACT
|
||||
#ifdef CONFIG_NET_INGRESS
|
||||
static struct static_key ingress_needed __read_mostly;
|
||||
|
||||
void net_inc_ingress_queue(void)
|
||||
@@ -2343,6 +2348,34 @@ void netif_device_attach(struct net_device *dev)
|
||||
}
|
||||
EXPORT_SYMBOL(netif_device_attach);
|
||||
|
||||
/*
|
||||
* Returns a Tx hash based on the given packet descriptor a Tx queues' number
|
||||
* to be used as a distribution range.
|
||||
*/
|
||||
u16 __skb_tx_hash(const struct net_device *dev, struct sk_buff *skb,
|
||||
unsigned int num_tx_queues)
|
||||
{
|
||||
u32 hash;
|
||||
u16 qoffset = 0;
|
||||
u16 qcount = num_tx_queues;
|
||||
|
||||
if (skb_rx_queue_recorded(skb)) {
|
||||
hash = skb_get_rx_queue(skb);
|
||||
while (unlikely(hash >= num_tx_queues))
|
||||
hash -= num_tx_queues;
|
||||
return hash;
|
||||
}
|
||||
|
||||
if (dev->num_tc) {
|
||||
u8 tc = netdev_get_prio_tc_map(dev, skb->priority);
|
||||
qoffset = dev->tc_to_txq[tc].offset;
|
||||
qcount = dev->tc_to_txq[tc].count;
|
||||
}
|
||||
|
||||
return (u16) reciprocal_scale(skb_get_hash(skb), qcount) + qoffset;
|
||||
}
|
||||
EXPORT_SYMBOL(__skb_tx_hash);
|
||||
|
||||
static void skb_warn_bad_offload(const struct sk_buff *skb)
|
||||
{
|
||||
static const netdev_features_t null_features = 0;
|
||||
@@ -2901,6 +2934,84 @@ int dev_loopback_xmit(struct sock *sk, struct sk_buff *skb)
|
||||
}
|
||||
EXPORT_SYMBOL(dev_loopback_xmit);
|
||||
|
||||
static inline int get_xps_queue(struct net_device *dev, struct sk_buff *skb)
|
||||
{
|
||||
#ifdef CONFIG_XPS
|
||||
struct xps_dev_maps *dev_maps;
|
||||
struct xps_map *map;
|
||||
int queue_index = -1;
|
||||
|
||||
rcu_read_lock();
|
||||
dev_maps = rcu_dereference(dev->xps_maps);
|
||||
if (dev_maps) {
|
||||
map = rcu_dereference(
|
||||
dev_maps->cpu_map[skb->sender_cpu - 1]);
|
||||
if (map) {
|
||||
if (map->len == 1)
|
||||
queue_index = map->queues[0];
|
||||
else
|
||||
queue_index = map->queues[reciprocal_scale(skb_get_hash(skb),
|
||||
map->len)];
|
||||
if (unlikely(queue_index >= dev->real_num_tx_queues))
|
||||
queue_index = -1;
|
||||
}
|
||||
}
|
||||
rcu_read_unlock();
|
||||
|
||||
return queue_index;
|
||||
#else
|
||||
return -1;
|
||||
#endif
|
||||
}
|
||||
|
||||
static u16 __netdev_pick_tx(struct net_device *dev, struct sk_buff *skb)
|
||||
{
|
||||
struct sock *sk = skb->sk;
|
||||
int queue_index = sk_tx_queue_get(sk);
|
||||
|
||||
if (queue_index < 0 || skb->ooo_okay ||
|
||||
queue_index >= dev->real_num_tx_queues) {
|
||||
int new_index = get_xps_queue(dev, skb);
|
||||
if (new_index < 0)
|
||||
new_index = skb_tx_hash(dev, skb);
|
||||
|
||||
if (queue_index != new_index && sk &&
|
||||
rcu_access_pointer(sk->sk_dst_cache))
|
||||
sk_tx_queue_set(sk, new_index);
|
||||
|
||||
queue_index = new_index;
|
||||
}
|
||||
|
||||
return queue_index;
|
||||
}
|
||||
|
||||
struct netdev_queue *netdev_pick_tx(struct net_device *dev,
|
||||
struct sk_buff *skb,
|
||||
void *accel_priv)
|
||||
{
|
||||
int queue_index = 0;
|
||||
|
||||
#ifdef CONFIG_XPS
|
||||
if (skb->sender_cpu == 0)
|
||||
skb->sender_cpu = raw_smp_processor_id() + 1;
|
||||
#endif
|
||||
|
||||
if (dev->real_num_tx_queues != 1) {
|
||||
const struct net_device_ops *ops = dev->netdev_ops;
|
||||
if (ops->ndo_select_queue)
|
||||
queue_index = ops->ndo_select_queue(dev, skb, accel_priv,
|
||||
__netdev_pick_tx);
|
||||
else
|
||||
queue_index = __netdev_pick_tx(dev, skb);
|
||||
|
||||
if (!accel_priv)
|
||||
queue_index = netdev_cap_txqueue(dev, queue_index);
|
||||
}
|
||||
|
||||
skb_set_queue_mapping(skb, queue_index);
|
||||
return netdev_get_tx_queue(dev, queue_index);
|
||||
}
|
||||
|
||||
/**
|
||||
* __dev_queue_xmit - transmit a buffer
|
||||
* @skb: buffer to transmit
|
||||
@@ -3513,66 +3624,47 @@ int (*br_fdb_test_addr_hook)(struct net_device *dev,
|
||||
EXPORT_SYMBOL_GPL(br_fdb_test_addr_hook);
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_NET_CLS_ACT
|
||||
/* TODO: Maybe we should just force sch_ingress to be compiled in
|
||||
* when CONFIG_NET_CLS_ACT is? otherwise some useless instructions
|
||||
* a compare and 2 stores extra right now if we dont have it on
|
||||
* but have CONFIG_NET_CLS_ACT
|
||||
* NOTE: This doesn't stop any functionality; if you dont have
|
||||
* the ingress scheduler, you just can't add policies on ingress.
|
||||
*
|
||||
*/
|
||||
static int ing_filter(struct sk_buff *skb, struct netdev_queue *rxq)
|
||||
{
|
||||
struct net_device *dev = skb->dev;
|
||||
u32 ttl = G_TC_RTTL(skb->tc_verd);
|
||||
int result = TC_ACT_OK;
|
||||
struct Qdisc *q;
|
||||
|
||||
if (unlikely(MAX_RED_LOOP < ttl++)) {
|
||||
net_warn_ratelimited("Redir loop detected Dropping packet (%d->%d)\n",
|
||||
skb->skb_iif, dev->ifindex);
|
||||
return TC_ACT_SHOT;
|
||||
}
|
||||
|
||||
skb->tc_verd = SET_TC_RTTL(skb->tc_verd, ttl);
|
||||
skb->tc_verd = SET_TC_AT(skb->tc_verd, AT_INGRESS);
|
||||
|
||||
q = rcu_dereference(rxq->qdisc);
|
||||
if (q != &noop_qdisc) {
|
||||
spin_lock(qdisc_lock(q));
|
||||
if (likely(!test_bit(__QDISC_STATE_DEACTIVATED, &q->state)))
|
||||
result = qdisc_enqueue_root(skb, q);
|
||||
spin_unlock(qdisc_lock(q));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static inline struct sk_buff *handle_ing(struct sk_buff *skb,
|
||||
struct packet_type **pt_prev,
|
||||
int *ret, struct net_device *orig_dev)
|
||||
{
|
||||
struct netdev_queue *rxq = rcu_dereference(skb->dev->ingress_queue);
|
||||
#ifdef CONFIG_NET_CLS_ACT
|
||||
struct tcf_proto *cl = rcu_dereference_bh(skb->dev->ingress_cl_list);
|
||||
struct tcf_result cl_res;
|
||||
|
||||
if (!rxq || rcu_access_pointer(rxq->qdisc) == &noop_qdisc)
|
||||
/* If there's at least one ingress present somewhere (so
|
||||
* we get here via enabled static key), remaining devices
|
||||
* that are not configured with an ingress qdisc will bail
|
||||
* out here.
|
||||
*/
|
||||
if (!cl)
|
||||
return skb;
|
||||
|
||||
if (*pt_prev) {
|
||||
*ret = deliver_skb(skb, *pt_prev, orig_dev);
|
||||
*pt_prev = NULL;
|
||||
}
|
||||
|
||||
switch (ing_filter(skb, rxq)) {
|
||||
qdisc_skb_cb(skb)->pkt_len = skb->len;
|
||||
skb->tc_verd = SET_TC_AT(skb->tc_verd, AT_INGRESS);
|
||||
qdisc_bstats_update_cpu(cl->q, skb);
|
||||
|
||||
switch (tc_classify(skb, cl, &cl_res)) {
|
||||
case TC_ACT_OK:
|
||||
case TC_ACT_RECLASSIFY:
|
||||
skb->tc_index = TC_H_MIN(cl_res.classid);
|
||||
break;
|
||||
case TC_ACT_SHOT:
|
||||
qdisc_qstats_drop_cpu(cl->q);
|
||||
case TC_ACT_STOLEN:
|
||||
case TC_ACT_QUEUED:
|
||||
kfree_skb(skb);
|
||||
return NULL;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
#endif /* CONFIG_NET_CLS_ACT */
|
||||
return skb;
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* netdev_rx_handler_register - register receive handler
|
||||
@@ -3645,6 +3737,22 @@ static bool skb_pfmemalloc_protocol(struct sk_buff *skb)
|
||||
}
|
||||
}
|
||||
|
||||
static inline int nf_ingress(struct sk_buff *skb, struct packet_type **pt_prev,
|
||||
int *ret, struct net_device *orig_dev)
|
||||
{
|
||||
#ifdef CONFIG_NETFILTER_INGRESS
|
||||
if (nf_hook_ingress_active(skb)) {
|
||||
if (*pt_prev) {
|
||||
*ret = deliver_skb(skb, *pt_prev, orig_dev);
|
||||
*pt_prev = NULL;
|
||||
}
|
||||
|
||||
return nf_hook_ingress(skb);
|
||||
}
|
||||
#endif /* CONFIG_NETFILTER_INGRESS */
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __netif_receive_skb_core(struct sk_buff *skb, bool pfmemalloc)
|
||||
{
|
||||
struct packet_type *ptype, *pt_prev;
|
||||
@@ -3704,13 +3812,17 @@ static int __netif_receive_skb_core(struct sk_buff *skb, bool pfmemalloc)
|
||||
}
|
||||
|
||||
skip_taps:
|
||||
#ifdef CONFIG_NET_CLS_ACT
|
||||
#ifdef CONFIG_NET_INGRESS
|
||||
if (static_key_false(&ingress_needed)) {
|
||||
skb = handle_ing(skb, &pt_prev, &ret, orig_dev);
|
||||
if (!skb)
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
if (nf_ingress(skb, &pt_prev, &ret, orig_dev) < 0)
|
||||
goto unlock;
|
||||
}
|
||||
#endif
|
||||
#ifdef CONFIG_NET_CLS_ACT
|
||||
skb->tc_verd = 0;
|
||||
ncls:
|
||||
#endif
|
||||
@@ -6313,6 +6425,17 @@ static int netif_alloc_netdev_queues(struct net_device *dev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
void netif_tx_stop_all_queues(struct net_device *dev)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < dev->num_tx_queues; i++) {
|
||||
struct netdev_queue *txq = netdev_get_tx_queue(dev, i);
|
||||
netif_tx_stop_queue(txq);
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL(netif_tx_stop_all_queues);
|
||||
|
||||
/**
|
||||
* register_netdevice - register a network device
|
||||
* @dev: device to register
|
||||
@@ -6862,6 +6985,9 @@ struct net_device *alloc_netdev_mqs(int sizeof_priv, const char *name,
|
||||
dev->group = INIT_NETDEV_GROUP;
|
||||
if (!dev->ethtool_ops)
|
||||
dev->ethtool_ops = &default_ethtool_ops;
|
||||
|
||||
nf_hook_ingress_init(dev);
|
||||
|
||||
return dev;
|
||||
|
||||
free_all:
|
||||
|
||||
@@ -98,7 +98,6 @@ 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_SWITCH_OFFLOAD_BIT] = "hw-switch-offload",
|
||||
};
|
||||
|
||||
static const char
|
||||
@@ -107,6 +106,13 @@ rss_hash_func_strings[ETH_RSS_HASH_FUNCS_COUNT][ETH_GSTRING_LEN] = {
|
||||
[ETH_RSS_HASH_XOR_BIT] = "xor",
|
||||
};
|
||||
|
||||
static const char
|
||||
tunable_strings[__ETHTOOL_TUNABLE_COUNT][ETH_GSTRING_LEN] = {
|
||||
[ETHTOOL_ID_UNSPEC] = "Unspec",
|
||||
[ETHTOOL_RX_COPYBREAK] = "rx-copybreak",
|
||||
[ETHTOOL_TX_COPYBREAK] = "tx-copybreak",
|
||||
};
|
||||
|
||||
static int ethtool_get_features(struct net_device *dev, void __user *useraddr)
|
||||
{
|
||||
struct ethtool_gfeatures cmd = {
|
||||
@@ -195,6 +201,9 @@ static int __ethtool_get_sset_count(struct net_device *dev, int sset)
|
||||
if (sset == ETH_SS_RSS_HASH_FUNCS)
|
||||
return ARRAY_SIZE(rss_hash_func_strings);
|
||||
|
||||
if (sset == ETH_SS_TUNABLES)
|
||||
return ARRAY_SIZE(tunable_strings);
|
||||
|
||||
if (ops->get_sset_count && ops->get_strings)
|
||||
return ops->get_sset_count(dev, sset);
|
||||
else
|
||||
@@ -212,6 +221,8 @@ static void __ethtool_get_strings(struct net_device *dev,
|
||||
else if (stringset == ETH_SS_RSS_HASH_FUNCS)
|
||||
memcpy(data, rss_hash_func_strings,
|
||||
sizeof(rss_hash_func_strings));
|
||||
else if (stringset == ETH_SS_TUNABLES)
|
||||
memcpy(data, tunable_strings, sizeof(tunable_strings));
|
||||
else
|
||||
/* ops->get_strings is valid because checked earlier */
|
||||
ops->get_strings(dev, stringset, data);
|
||||
|
||||
@@ -36,6 +36,7 @@
|
||||
#include <net/netlink.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <net/sock.h>
|
||||
#include <net/flow_dissector.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/timer.h>
|
||||
#include <asm/uaccess.h>
|
||||
@@ -45,6 +46,7 @@
|
||||
#include <linux/seccomp.h>
|
||||
#include <linux/if_vlan.h>
|
||||
#include <linux/bpf.h>
|
||||
#include <net/sch_generic.h>
|
||||
|
||||
/**
|
||||
* sk_filter - run a packet through a socket filter
|
||||
@@ -355,8 +357,8 @@ static bool convert_bpf_extensions(struct sock_filter *fp,
|
||||
* for socket filters: ctx == 'struct sk_buff *', for seccomp:
|
||||
* ctx == 'struct seccomp_data *'.
|
||||
*/
|
||||
int bpf_convert_filter(struct sock_filter *prog, int len,
|
||||
struct bpf_insn *new_prog, int *new_len)
|
||||
static int bpf_convert_filter(struct sock_filter *prog, int len,
|
||||
struct bpf_insn *new_prog, int *new_len)
|
||||
{
|
||||
int new_flen = 0, pass = 0, target, i;
|
||||
struct bpf_insn *new_insn;
|
||||
@@ -371,7 +373,8 @@ int bpf_convert_filter(struct sock_filter *prog, int len,
|
||||
return -EINVAL;
|
||||
|
||||
if (new_prog) {
|
||||
addrs = kcalloc(len, sizeof(*addrs), GFP_KERNEL);
|
||||
addrs = kcalloc(len, sizeof(*addrs),
|
||||
GFP_KERNEL | __GFP_NOWARN);
|
||||
if (!addrs)
|
||||
return -ENOMEM;
|
||||
}
|
||||
@@ -751,7 +754,8 @@ static bool chk_code_allowed(u16 code_to_probe)
|
||||
*
|
||||
* Returns 0 if the rule set is legal or -EINVAL if not.
|
||||
*/
|
||||
int bpf_check_classic(const struct sock_filter *filter, unsigned int flen)
|
||||
static int bpf_check_classic(const struct sock_filter *filter,
|
||||
unsigned int flen)
|
||||
{
|
||||
bool anc_found;
|
||||
int pc;
|
||||
@@ -825,7 +829,6 @@ int bpf_check_classic(const struct sock_filter *filter, unsigned int flen)
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
EXPORT_SYMBOL(bpf_check_classic);
|
||||
|
||||
static int bpf_prog_store_orig_filter(struct bpf_prog *fp,
|
||||
const struct sock_fprog *fprog)
|
||||
@@ -839,7 +842,9 @@ static int bpf_prog_store_orig_filter(struct bpf_prog *fp,
|
||||
|
||||
fkprog = fp->orig_prog;
|
||||
fkprog->len = fprog->len;
|
||||
fkprog->filter = kmemdup(fp->insns, fsize, GFP_KERNEL);
|
||||
|
||||
fkprog->filter = kmemdup(fp->insns, fsize,
|
||||
GFP_KERNEL | __GFP_NOWARN);
|
||||
if (!fkprog->filter) {
|
||||
kfree(fp->orig_prog);
|
||||
return -ENOMEM;
|
||||
@@ -941,7 +946,7 @@ static struct bpf_prog *bpf_migrate_filter(struct bpf_prog *fp)
|
||||
* pass. At this time, the user BPF is stored in fp->insns.
|
||||
*/
|
||||
old_prog = kmemdup(fp->insns, old_len * sizeof(struct sock_filter),
|
||||
GFP_KERNEL);
|
||||
GFP_KERNEL | __GFP_NOWARN);
|
||||
if (!old_prog) {
|
||||
err = -ENOMEM;
|
||||
goto out_err;
|
||||
@@ -988,7 +993,8 @@ static struct bpf_prog *bpf_migrate_filter(struct bpf_prog *fp)
|
||||
return ERR_PTR(err);
|
||||
}
|
||||
|
||||
static struct bpf_prog *bpf_prepare_filter(struct bpf_prog *fp)
|
||||
static struct bpf_prog *bpf_prepare_filter(struct bpf_prog *fp,
|
||||
bpf_aux_classic_check_t trans)
|
||||
{
|
||||
int err;
|
||||
|
||||
@@ -1001,6 +1007,17 @@ static struct bpf_prog *bpf_prepare_filter(struct bpf_prog *fp)
|
||||
return ERR_PTR(err);
|
||||
}
|
||||
|
||||
/* There might be additional checks and transformations
|
||||
* needed on classic filters, f.e. in case of seccomp.
|
||||
*/
|
||||
if (trans) {
|
||||
err = trans(fp->insns, fp->len);
|
||||
if (err) {
|
||||
__bpf_prog_release(fp);
|
||||
return ERR_PTR(err);
|
||||
}
|
||||
}
|
||||
|
||||
/* Probe if we can JIT compile the filter and if so, do
|
||||
* the compilation of the filter.
|
||||
*/
|
||||
@@ -1050,7 +1067,7 @@ int bpf_prog_create(struct bpf_prog **pfp, struct sock_fprog_kern *fprog)
|
||||
/* bpf_prepare_filter() already takes care of freeing
|
||||
* memory in case something goes wrong.
|
||||
*/
|
||||
fp = bpf_prepare_filter(fp);
|
||||
fp = bpf_prepare_filter(fp, NULL);
|
||||
if (IS_ERR(fp))
|
||||
return PTR_ERR(fp);
|
||||
|
||||
@@ -1059,6 +1076,53 @@ int bpf_prog_create(struct bpf_prog **pfp, struct sock_fprog_kern *fprog)
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(bpf_prog_create);
|
||||
|
||||
/**
|
||||
* bpf_prog_create_from_user - create an unattached filter from user buffer
|
||||
* @pfp: the unattached filter that is created
|
||||
* @fprog: the filter program
|
||||
* @trans: post-classic verifier transformation handler
|
||||
*
|
||||
* This function effectively does the same as bpf_prog_create(), only
|
||||
* that it builds up its insns buffer from user space provided buffer.
|
||||
* It also allows for passing a bpf_aux_classic_check_t handler.
|
||||
*/
|
||||
int bpf_prog_create_from_user(struct bpf_prog **pfp, struct sock_fprog *fprog,
|
||||
bpf_aux_classic_check_t trans)
|
||||
{
|
||||
unsigned int fsize = bpf_classic_proglen(fprog);
|
||||
struct bpf_prog *fp;
|
||||
|
||||
/* Make sure new filter is there and in the right amounts. */
|
||||
if (fprog->filter == NULL)
|
||||
return -EINVAL;
|
||||
|
||||
fp = bpf_prog_alloc(bpf_prog_size(fprog->len), 0);
|
||||
if (!fp)
|
||||
return -ENOMEM;
|
||||
|
||||
if (copy_from_user(fp->insns, fprog->filter, fsize)) {
|
||||
__bpf_prog_free(fp);
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
fp->len = fprog->len;
|
||||
/* Since unattached filters are not copied back to user
|
||||
* space through sk_get_filter(), we do not need to hold
|
||||
* a copy here, and can spare us the work.
|
||||
*/
|
||||
fp->orig_prog = NULL;
|
||||
|
||||
/* bpf_prepare_filter() already takes care of freeing
|
||||
* memory in case something goes wrong.
|
||||
*/
|
||||
fp = bpf_prepare_filter(fp, trans);
|
||||
if (IS_ERR(fp))
|
||||
return PTR_ERR(fp);
|
||||
|
||||
*pfp = fp;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void bpf_prog_destroy(struct bpf_prog *fp)
|
||||
{
|
||||
__bpf_prog_release(fp);
|
||||
@@ -1135,7 +1199,7 @@ int sk_attach_filter(struct sock_fprog *fprog, struct sock *sk)
|
||||
/* bpf_prepare_filter() already takes care of freeing
|
||||
* memory in case something goes wrong.
|
||||
*/
|
||||
prog = bpf_prepare_filter(prog);
|
||||
prog = bpf_prepare_filter(prog, NULL);
|
||||
if (IS_ERR(prog))
|
||||
return PTR_ERR(prog);
|
||||
|
||||
@@ -1175,21 +1239,6 @@ int sk_attach_bpf(u32 ufd, struct sock *sk)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* bpf_skb_clone_not_writable - is the header of a clone not writable
|
||||
* @skb: buffer to check
|
||||
* @len: length up to which to write, can be negative
|
||||
*
|
||||
* Returns true if modifying the header part of the cloned buffer
|
||||
* does require the data to be copied. I.e. this version works with
|
||||
* negative lengths needed for eBPF case!
|
||||
*/
|
||||
static bool bpf_skb_clone_unwritable(const struct sk_buff *skb, int len)
|
||||
{
|
||||
return skb_header_cloned(skb) ||
|
||||
(int) skb_headroom(skb) + len > skb->hdr_len;
|
||||
}
|
||||
|
||||
#define BPF_RECOMPUTE_CSUM(flags) ((flags) & 1)
|
||||
|
||||
static u64 bpf_skb_store_bytes(u64 r1, u64 r2, u64 r3, u64 r4, u64 flags)
|
||||
@@ -1212,9 +1261,8 @@ static u64 bpf_skb_store_bytes(u64 r1, u64 r2, u64 r3, u64 r4, u64 flags)
|
||||
if (unlikely((u32) offset > 0xffff || len > sizeof(buf)))
|
||||
return -EFAULT;
|
||||
|
||||
offset -= skb->data - skb_mac_header(skb);
|
||||
if (unlikely(skb_cloned(skb) &&
|
||||
bpf_skb_clone_unwritable(skb, offset + len)))
|
||||
!skb_clone_writable(skb, offset + len)))
|
||||
return -EFAULT;
|
||||
|
||||
ptr = skb_header_pointer(skb, offset, len, buf);
|
||||
@@ -1258,9 +1306,8 @@ static u64 bpf_l3_csum_replace(u64 r1, u64 r2, u64 from, u64 to, u64 flags)
|
||||
if (unlikely((u32) offset > 0xffff))
|
||||
return -EFAULT;
|
||||
|
||||
offset -= skb->data - skb_mac_header(skb);
|
||||
if (unlikely(skb_cloned(skb) &&
|
||||
bpf_skb_clone_unwritable(skb, offset + sizeof(sum))))
|
||||
!skb_clone_writable(skb, offset + sizeof(sum))))
|
||||
return -EFAULT;
|
||||
|
||||
ptr = skb_header_pointer(skb, offset, sizeof(sum), &sum);
|
||||
@@ -1306,9 +1353,8 @@ static u64 bpf_l4_csum_replace(u64 r1, u64 r2, u64 from, u64 to, u64 flags)
|
||||
if (unlikely((u32) offset > 0xffff))
|
||||
return -EFAULT;
|
||||
|
||||
offset -= skb->data - skb_mac_header(skb);
|
||||
if (unlikely(skb_cloned(skb) &&
|
||||
bpf_skb_clone_unwritable(skb, offset + sizeof(sum))))
|
||||
!skb_clone_writable(skb, offset + sizeof(sum))))
|
||||
return -EFAULT;
|
||||
|
||||
ptr = skb_header_pointer(skb, offset, sizeof(sum), &sum);
|
||||
@@ -1344,6 +1390,40 @@ const struct bpf_func_proto bpf_l4_csum_replace_proto = {
|
||||
.arg5_type = ARG_ANYTHING,
|
||||
};
|
||||
|
||||
#define BPF_IS_REDIRECT_INGRESS(flags) ((flags) & 1)
|
||||
|
||||
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;
|
||||
struct net_device *dev;
|
||||
|
||||
dev = dev_get_by_index_rcu(dev_net(skb->dev), ifindex);
|
||||
if (unlikely(!dev))
|
||||
return -EINVAL;
|
||||
|
||||
if (unlikely(!(dev->flags & IFF_UP)))
|
||||
return -EINVAL;
|
||||
|
||||
skb2 = skb_clone(skb, GFP_ATOMIC);
|
||||
if (unlikely(!skb2))
|
||||
return -ENOMEM;
|
||||
|
||||
if (BPF_IS_REDIRECT_INGRESS(flags))
|
||||
return dev_forward_skb(dev, skb2);
|
||||
|
||||
skb2->dev = dev;
|
||||
return dev_queue_xmit(skb2);
|
||||
}
|
||||
|
||||
const struct bpf_func_proto bpf_clone_redirect_proto = {
|
||||
.func = bpf_clone_redirect,
|
||||
.gpl_only = false,
|
||||
.ret_type = RET_INTEGER,
|
||||
.arg1_type = ARG_PTR_TO_CTX,
|
||||
.arg2_type = ARG_ANYTHING,
|
||||
.arg3_type = ARG_ANYTHING,
|
||||
};
|
||||
|
||||
static const struct bpf_func_proto *
|
||||
sk_filter_func_proto(enum bpf_func_id func_id)
|
||||
{
|
||||
@@ -1358,6 +1438,12 @@ sk_filter_func_proto(enum bpf_func_id func_id)
|
||||
return &bpf_get_prandom_u32_proto;
|
||||
case BPF_FUNC_get_smp_processor_id:
|
||||
return &bpf_get_smp_processor_id_proto;
|
||||
case BPF_FUNC_tail_call:
|
||||
return &bpf_tail_call_proto;
|
||||
case BPF_FUNC_ktime_get_ns:
|
||||
return &bpf_ktime_get_ns_proto;
|
||||
case BPF_FUNC_trace_printk:
|
||||
return bpf_get_trace_printk_proto();
|
||||
default:
|
||||
return NULL;
|
||||
}
|
||||
@@ -1373,18 +1459,15 @@ tc_cls_act_func_proto(enum bpf_func_id func_id)
|
||||
return &bpf_l3_csum_replace_proto;
|
||||
case BPF_FUNC_l4_csum_replace:
|
||||
return &bpf_l4_csum_replace_proto;
|
||||
case BPF_FUNC_clone_redirect:
|
||||
return &bpf_clone_redirect_proto;
|
||||
default:
|
||||
return sk_filter_func_proto(func_id);
|
||||
}
|
||||
}
|
||||
|
||||
static bool sk_filter_is_valid_access(int off, int size,
|
||||
enum bpf_access_type type)
|
||||
static bool __is_valid_access(int off, int size, enum bpf_access_type type)
|
||||
{
|
||||
/* only read is allowed */
|
||||
if (type != BPF_READ)
|
||||
return false;
|
||||
|
||||
/* check bounds */
|
||||
if (off < 0 || off >= sizeof(struct __sk_buff))
|
||||
return false;
|
||||
@@ -1400,8 +1483,42 @@ static bool sk_filter_is_valid_access(int off, int size,
|
||||
return true;
|
||||
}
|
||||
|
||||
static u32 sk_filter_convert_ctx_access(int dst_reg, int src_reg, int ctx_off,
|
||||
struct bpf_insn *insn_buf)
|
||||
static bool sk_filter_is_valid_access(int off, int size,
|
||||
enum bpf_access_type type)
|
||||
{
|
||||
if (type == BPF_WRITE) {
|
||||
switch (off) {
|
||||
case offsetof(struct __sk_buff, cb[0]) ...
|
||||
offsetof(struct __sk_buff, cb[4]):
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return __is_valid_access(off, size, type);
|
||||
}
|
||||
|
||||
static bool tc_cls_act_is_valid_access(int off, int size,
|
||||
enum bpf_access_type type)
|
||||
{
|
||||
if (type == BPF_WRITE) {
|
||||
switch (off) {
|
||||
case offsetof(struct __sk_buff, mark):
|
||||
case offsetof(struct __sk_buff, tc_index):
|
||||
case offsetof(struct __sk_buff, cb[0]) ...
|
||||
offsetof(struct __sk_buff, cb[4]):
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return __is_valid_access(off, size, type);
|
||||
}
|
||||
|
||||
static u32 bpf_net_convert_ctx_access(enum bpf_access_type type, int dst_reg,
|
||||
int src_reg, int ctx_off,
|
||||
struct bpf_insn *insn_buf)
|
||||
{
|
||||
struct bpf_insn *insn = insn_buf;
|
||||
|
||||
@@ -1434,8 +1551,34 @@ static u32 sk_filter_convert_ctx_access(int dst_reg, int src_reg, int ctx_off,
|
||||
offsetof(struct sk_buff, priority));
|
||||
break;
|
||||
|
||||
case offsetof(struct __sk_buff, ingress_ifindex):
|
||||
BUILD_BUG_ON(FIELD_SIZEOF(struct sk_buff, skb_iif) != 4);
|
||||
|
||||
*insn++ = BPF_LDX_MEM(BPF_W, dst_reg, src_reg,
|
||||
offsetof(struct sk_buff, skb_iif));
|
||||
break;
|
||||
|
||||
case offsetof(struct __sk_buff, ifindex):
|
||||
BUILD_BUG_ON(FIELD_SIZEOF(struct net_device, ifindex) != 4);
|
||||
|
||||
*insn++ = BPF_LDX_MEM(bytes_to_bpf_size(FIELD_SIZEOF(struct sk_buff, dev)),
|
||||
dst_reg, src_reg,
|
||||
offsetof(struct sk_buff, dev));
|
||||
*insn++ = BPF_JMP_IMM(BPF_JEQ, dst_reg, 0, 1);
|
||||
*insn++ = BPF_LDX_MEM(BPF_W, dst_reg, dst_reg,
|
||||
offsetof(struct net_device, ifindex));
|
||||
break;
|
||||
|
||||
case offsetof(struct __sk_buff, mark):
|
||||
return convert_skb_access(SKF_AD_MARK, dst_reg, src_reg, insn);
|
||||
BUILD_BUG_ON(FIELD_SIZEOF(struct sk_buff, mark) != 4);
|
||||
|
||||
if (type == BPF_WRITE)
|
||||
*insn++ = BPF_STX_MEM(BPF_W, dst_reg, src_reg,
|
||||
offsetof(struct sk_buff, mark));
|
||||
else
|
||||
*insn++ = BPF_LDX_MEM(BPF_W, dst_reg, src_reg,
|
||||
offsetof(struct sk_buff, mark));
|
||||
break;
|
||||
|
||||
case offsetof(struct __sk_buff, pkt_type):
|
||||
return convert_skb_access(SKF_AD_PKTTYPE, dst_reg, src_reg, insn);
|
||||
@@ -1450,6 +1593,38 @@ static u32 sk_filter_convert_ctx_access(int dst_reg, int src_reg, int ctx_off,
|
||||
case offsetof(struct __sk_buff, vlan_tci):
|
||||
return convert_skb_access(SKF_AD_VLAN_TAG,
|
||||
dst_reg, src_reg, insn);
|
||||
|
||||
case offsetof(struct __sk_buff, cb[0]) ...
|
||||
offsetof(struct __sk_buff, cb[4]):
|
||||
BUILD_BUG_ON(FIELD_SIZEOF(struct qdisc_skb_cb, data) < 20);
|
||||
|
||||
ctx_off -= offsetof(struct __sk_buff, cb[0]);
|
||||
ctx_off += offsetof(struct sk_buff, cb);
|
||||
ctx_off += offsetof(struct qdisc_skb_cb, data);
|
||||
if (type == BPF_WRITE)
|
||||
*insn++ = BPF_STX_MEM(BPF_W, dst_reg, src_reg, ctx_off);
|
||||
else
|
||||
*insn++ = BPF_LDX_MEM(BPF_W, dst_reg, src_reg, ctx_off);
|
||||
break;
|
||||
|
||||
case offsetof(struct __sk_buff, tc_index):
|
||||
#ifdef CONFIG_NET_SCHED
|
||||
BUILD_BUG_ON(FIELD_SIZEOF(struct sk_buff, tc_index) != 2);
|
||||
|
||||
if (type == BPF_WRITE)
|
||||
*insn++ = BPF_STX_MEM(BPF_H, dst_reg, src_reg,
|
||||
offsetof(struct sk_buff, tc_index));
|
||||
else
|
||||
*insn++ = BPF_LDX_MEM(BPF_H, dst_reg, src_reg,
|
||||
offsetof(struct sk_buff, tc_index));
|
||||
break;
|
||||
#else
|
||||
if (type == BPF_WRITE)
|
||||
*insn++ = BPF_MOV64_REG(dst_reg, dst_reg);
|
||||
else
|
||||
*insn++ = BPF_MOV64_IMM(dst_reg, 0);
|
||||
break;
|
||||
#endif
|
||||
}
|
||||
|
||||
return insn - insn_buf;
|
||||
@@ -1458,13 +1633,13 @@ static u32 sk_filter_convert_ctx_access(int dst_reg, int src_reg, int ctx_off,
|
||||
static const struct bpf_verifier_ops sk_filter_ops = {
|
||||
.get_func_proto = sk_filter_func_proto,
|
||||
.is_valid_access = sk_filter_is_valid_access,
|
||||
.convert_ctx_access = sk_filter_convert_ctx_access,
|
||||
.convert_ctx_access = bpf_net_convert_ctx_access,
|
||||
};
|
||||
|
||||
static const struct bpf_verifier_ops tc_cls_act_ops = {
|
||||
.get_func_proto = tc_cls_act_func_proto,
|
||||
.is_valid_access = sk_filter_is_valid_access,
|
||||
.convert_ctx_access = sk_filter_convert_ctx_access,
|
||||
.is_valid_access = tc_cls_act_is_valid_access,
|
||||
.convert_ctx_access = bpf_net_convert_ctx_access,
|
||||
};
|
||||
|
||||
static struct bpf_prog_type_list sk_filter_type __read_mostly = {
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <linux/export.h>
|
||||
#include <linux/ip.h>
|
||||
@@ -12,20 +13,61 @@
|
||||
#include <linux/if_tunnel.h>
|
||||
#include <linux/if_pppox.h>
|
||||
#include <linux/ppp_defs.h>
|
||||
#include <net/flow_keys.h>
|
||||
#include <linux/stddef.h>
|
||||
#include <linux/if_ether.h>
|
||||
#include <linux/mpls.h>
|
||||
#include <net/flow_dissector.h>
|
||||
#include <scsi/fc/fc_fcoe.h>
|
||||
|
||||
/* copy saddr & daddr, possibly using 64bit load/store
|
||||
* Equivalent to : flow->src = iph->saddr;
|
||||
* flow->dst = iph->daddr;
|
||||
*/
|
||||
static void iph_to_flow_copy_addrs(struct flow_keys *flow, const struct iphdr *iph)
|
||||
static bool skb_flow_dissector_uses_key(struct flow_dissector *flow_dissector,
|
||||
enum flow_dissector_key_id key_id)
|
||||
{
|
||||
BUILD_BUG_ON(offsetof(typeof(*flow), dst) !=
|
||||
offsetof(typeof(*flow), src) + sizeof(flow->src));
|
||||
memcpy(&flow->src, &iph->saddr, sizeof(flow->src) + sizeof(flow->dst));
|
||||
return flow_dissector->used_keys & (1 << key_id);
|
||||
}
|
||||
|
||||
static void skb_flow_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)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
memset(flow_dissector, 0, sizeof(*flow_dissector));
|
||||
|
||||
for (i = 0; i < key_count; i++, key++) {
|
||||
/* User should make sure that every key target offset is withing
|
||||
* boundaries of unsigned short.
|
||||
*/
|
||||
BUG_ON(key->offset > USHRT_MAX);
|
||||
BUG_ON(skb_flow_dissector_uses_key(flow_dissector,
|
||||
key->key_id));
|
||||
|
||||
skb_flow_dissector_set_key(flow_dissector, key->key_id);
|
||||
flow_dissector->offset[key->key_id] = key->offset;
|
||||
}
|
||||
|
||||
/* Ensure that the dissector always includes control and basic key.
|
||||
* That way we are able to avoid handling lack of these in fast path.
|
||||
*/
|
||||
BUG_ON(!skb_flow_dissector_uses_key(flow_dissector,
|
||||
FLOW_DISSECTOR_KEY_CONTROL));
|
||||
BUG_ON(!skb_flow_dissector_uses_key(flow_dissector,
|
||||
FLOW_DISSECTOR_KEY_BASIC));
|
||||
}
|
||||
EXPORT_SYMBOL(skb_flow_dissector_init);
|
||||
|
||||
/**
|
||||
* __skb_flow_get_ports - extract the upper layer ports and return them
|
||||
* @skb: sk_buff to extract the ports from
|
||||
@@ -63,17 +105,30 @@ EXPORT_SYMBOL(__skb_flow_get_ports);
|
||||
/**
|
||||
* __skb_flow_dissect - extract the flow_keys struct and return it
|
||||
* @skb: sk_buff to extract the flow from, can be NULL if the rest are specified
|
||||
* @flow_dissector: list of keys to dissect
|
||||
* @target_container: target structure to put dissected values into
|
||||
* @data: raw buffer pointer to the packet, if NULL use skb->data
|
||||
* @proto: protocol for which to get the flow, if @data is NULL use skb->protocol
|
||||
* @nhoff: network header offset, if @data is NULL use skb_network_offset(skb)
|
||||
* @hlen: packet header length, if @data is NULL use skb_headlen(skb)
|
||||
*
|
||||
* The function will try to retrieve the struct flow_keys from either the skbuff
|
||||
* or a raw buffer specified by the rest parameters
|
||||
* The function will try to retrieve individual keys into target specified
|
||||
* by flow_dissector from either the skbuff or a raw buffer specified by the
|
||||
* rest parameters.
|
||||
*
|
||||
* Caller must take care of zeroing target container memory.
|
||||
*/
|
||||
bool __skb_flow_dissect(const struct sk_buff *skb, struct flow_keys *flow,
|
||||
bool __skb_flow_dissect(const struct sk_buff *skb,
|
||||
struct flow_dissector *flow_dissector,
|
||||
void *target_container,
|
||||
void *data, __be16 proto, int nhoff, int hlen)
|
||||
{
|
||||
struct flow_dissector_key_control *key_control;
|
||||
struct flow_dissector_key_basic *key_basic;
|
||||
struct flow_dissector_key_addrs *key_addrs;
|
||||
struct flow_dissector_key_ports *key_ports;
|
||||
struct flow_dissector_key_tags *key_tags;
|
||||
struct flow_dissector_key_keyid *key_keyid;
|
||||
u8 ip_proto;
|
||||
|
||||
if (!data) {
|
||||
@@ -83,7 +138,30 @@ bool __skb_flow_dissect(const struct sk_buff *skb, struct flow_keys *flow,
|
||||
hlen = skb_headlen(skb);
|
||||
}
|
||||
|
||||
memset(flow, 0, sizeof(*flow));
|
||||
/* It is ensured by skb_flow_dissector_init() that control key will
|
||||
* be always present.
|
||||
*/
|
||||
key_control = skb_flow_dissector_target(flow_dissector,
|
||||
FLOW_DISSECTOR_KEY_CONTROL,
|
||||
target_container);
|
||||
|
||||
/* It is ensured by skb_flow_dissector_init() that basic key will
|
||||
* be always present.
|
||||
*/
|
||||
key_basic = skb_flow_dissector_target(flow_dissector,
|
||||
FLOW_DISSECTOR_KEY_BASIC,
|
||||
target_container);
|
||||
|
||||
if (skb_flow_dissector_uses_key(flow_dissector,
|
||||
FLOW_DISSECTOR_KEY_ETH_ADDRS)) {
|
||||
struct ethhdr *eth = eth_hdr(skb);
|
||||
struct flow_dissector_key_eth_addrs *key_eth_addrs;
|
||||
|
||||
key_eth_addrs = skb_flow_dissector_target(flow_dissector,
|
||||
FLOW_DISSECTOR_KEY_ETH_ADDRS,
|
||||
target_container);
|
||||
memcpy(key_eth_addrs, ð->h_dest, sizeof(*key_eth_addrs));
|
||||
}
|
||||
|
||||
again:
|
||||
switch (proto) {
|
||||
@@ -100,14 +178,15 @@ bool __skb_flow_dissect(const struct sk_buff *skb, struct flow_keys *flow,
|
||||
if (ip_is_fragment(iph))
|
||||
ip_proto = 0;
|
||||
|
||||
/* skip the address processing if skb is NULL. The assumption
|
||||
* here is that if there is no skb we are not looking for flow
|
||||
* info but lengths and protocols.
|
||||
*/
|
||||
if (!skb)
|
||||
if (!skb_flow_dissector_uses_key(flow_dissector,
|
||||
FLOW_DISSECTOR_KEY_IPV4_ADDRS))
|
||||
break;
|
||||
|
||||
iph_to_flow_copy_addrs(flow, iph);
|
||||
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;
|
||||
break;
|
||||
}
|
||||
case htons(ETH_P_IPV6): {
|
||||
@@ -123,25 +202,27 @@ bool __skb_flow_dissect(const struct sk_buff *skb, struct flow_keys *flow,
|
||||
ip_proto = iph->nexthdr;
|
||||
nhoff += sizeof(struct ipv6hdr);
|
||||
|
||||
/* see comment above in IPv4 section */
|
||||
if (!skb)
|
||||
break;
|
||||
if (skb_flow_dissector_uses_key(flow_dissector,
|
||||
FLOW_DISSECTOR_KEY_IPV6_ADDRS)) {
|
||||
struct flow_dissector_key_ipv6_addrs *key_ipv6_addrs;
|
||||
|
||||
flow->src = (__force __be32)ipv6_addr_hash(&iph->saddr);
|
||||
flow->dst = (__force __be32)ipv6_addr_hash(&iph->daddr);
|
||||
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));
|
||||
key_control->addr_type = FLOW_DISSECTOR_KEY_IPV6_ADDRS;
|
||||
}
|
||||
|
||||
flow_label = ip6_flowlabel(iph);
|
||||
if (flow_label) {
|
||||
/* Awesome, IPv6 packet has a flow label so we can
|
||||
* use that to represent the ports without any
|
||||
* further dissection.
|
||||
*/
|
||||
flow->n_proto = proto;
|
||||
flow->ip_proto = ip_proto;
|
||||
flow->ports = flow_label;
|
||||
flow->thoff = (u16)nhoff;
|
||||
|
||||
return true;
|
||||
if (skb_flow_dissector_uses_key(flow_dissector,
|
||||
FLOW_DISSECTOR_KEY_FLOW_LABEL)) {
|
||||
key_tags = skb_flow_dissector_target(flow_dissector,
|
||||
FLOW_DISSECTOR_KEY_FLOW_LABEL,
|
||||
target_container);
|
||||
key_tags->flow_label = ntohl(flow_label);
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
@@ -155,6 +236,15 @@ bool __skb_flow_dissect(const struct sk_buff *skb, struct flow_keys *flow,
|
||||
if (!vlan)
|
||||
return false;
|
||||
|
||||
if (skb_flow_dissector_uses_key(flow_dissector,
|
||||
FLOW_DISSECTOR_KEY_VLANID)) {
|
||||
key_tags = skb_flow_dissector_target(flow_dissector,
|
||||
FLOW_DISSECTOR_KEY_VLANID,
|
||||
target_container);
|
||||
|
||||
key_tags->vlan_id = skb_vlan_tag_get_id(skb);
|
||||
}
|
||||
|
||||
proto = vlan->h_vlan_encapsulated_proto;
|
||||
nhoff += sizeof(*vlan);
|
||||
goto again;
|
||||
@@ -186,19 +276,58 @@ bool __skb_flow_dissect(const struct sk_buff *skb, struct flow_keys *flow,
|
||||
hdr = __skb_header_pointer(skb, nhoff, sizeof(_hdr), data, hlen, &_hdr);
|
||||
if (!hdr)
|
||||
return false;
|
||||
flow->src = hdr->srcnode;
|
||||
flow->dst = 0;
|
||||
flow->n_proto = proto;
|
||||
flow->thoff = (u16)nhoff;
|
||||
key_basic->n_proto = proto;
|
||||
key_control->thoff = (u16)nhoff;
|
||||
|
||||
if (skb_flow_dissector_uses_key(flow_dissector,
|
||||
FLOW_DISSECTOR_KEY_TIPC_ADDRS)) {
|
||||
key_addrs = skb_flow_dissector_target(flow_dissector,
|
||||
FLOW_DISSECTOR_KEY_TIPC_ADDRS,
|
||||
target_container);
|
||||
key_addrs->tipcaddrs.srcnode = hdr->srcnode;
|
||||
key_control->addr_type = FLOW_DISSECTOR_KEY_TIPC_ADDRS;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
case htons(ETH_P_MPLS_UC):
|
||||
case htons(ETH_P_MPLS_MC): {
|
||||
struct mpls_label *hdr, _hdr[2];
|
||||
mpls:
|
||||
hdr = __skb_header_pointer(skb, nhoff, sizeof(_hdr), data,
|
||||
hlen, &_hdr);
|
||||
if (!hdr)
|
||||
return false;
|
||||
|
||||
if ((ntohl(hdr[0].entry) & MPLS_LS_LABEL_MASK) >>
|
||||
MPLS_LS_LABEL_SHIFT == MPLS_LABEL_ENTROPY) {
|
||||
if (skb_flow_dissector_uses_key(flow_dissector,
|
||||
FLOW_DISSECTOR_KEY_MPLS_ENTROPY)) {
|
||||
key_keyid = skb_flow_dissector_target(flow_dissector,
|
||||
FLOW_DISSECTOR_KEY_MPLS_ENTROPY,
|
||||
target_container);
|
||||
key_keyid->keyid = hdr[1].entry &
|
||||
htonl(MPLS_LS_LABEL_MASK);
|
||||
}
|
||||
|
||||
key_basic->n_proto = proto;
|
||||
key_basic->ip_proto = ip_proto;
|
||||
key_control->thoff = (u16)nhoff;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
case htons(ETH_P_FCOE):
|
||||
flow->thoff = (u16)(nhoff + FCOE_HEADER_LEN);
|
||||
key_control->thoff = (u16)(nhoff + FCOE_HEADER_LEN);
|
||||
/* fall through */
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
ip_proto_again:
|
||||
switch (ip_proto) {
|
||||
case IPPROTO_GRE: {
|
||||
struct gre_hdr {
|
||||
@@ -213,30 +342,65 @@ bool __skb_flow_dissect(const struct sk_buff *skb, struct flow_keys *flow,
|
||||
* Only look inside GRE if version zero and no
|
||||
* routing
|
||||
*/
|
||||
if (!(hdr->flags & (GRE_VERSION|GRE_ROUTING))) {
|
||||
proto = hdr->proto;
|
||||
nhoff += 4;
|
||||
if (hdr->flags & GRE_CSUM)
|
||||
nhoff += 4;
|
||||
if (hdr->flags & GRE_KEY)
|
||||
nhoff += 4;
|
||||
if (hdr->flags & GRE_SEQ)
|
||||
nhoff += 4;
|
||||
if (proto == htons(ETH_P_TEB)) {
|
||||
const struct ethhdr *eth;
|
||||
struct ethhdr _eth;
|
||||
if (hdr->flags & (GRE_VERSION | GRE_ROUTING))
|
||||
break;
|
||||
|
||||
eth = __skb_header_pointer(skb, nhoff,
|
||||
sizeof(_eth),
|
||||
data, hlen, &_eth);
|
||||
if (!eth)
|
||||
return false;
|
||||
proto = eth->h_proto;
|
||||
nhoff += sizeof(*eth);
|
||||
proto = hdr->proto;
|
||||
nhoff += 4;
|
||||
if (hdr->flags & GRE_CSUM)
|
||||
nhoff += 4;
|
||||
if (hdr->flags & GRE_KEY) {
|
||||
const __be32 *keyid;
|
||||
__be32 _keyid;
|
||||
|
||||
keyid = __skb_header_pointer(skb, nhoff, sizeof(_keyid),
|
||||
data, hlen, &_keyid);
|
||||
|
||||
if (!keyid)
|
||||
return false;
|
||||
|
||||
if (skb_flow_dissector_uses_key(flow_dissector,
|
||||
FLOW_DISSECTOR_KEY_GRE_KEYID)) {
|
||||
key_keyid = skb_flow_dissector_target(flow_dissector,
|
||||
FLOW_DISSECTOR_KEY_GRE_KEYID,
|
||||
target_container);
|
||||
key_keyid->keyid = *keyid;
|
||||
}
|
||||
goto again;
|
||||
nhoff += 4;
|
||||
}
|
||||
break;
|
||||
if (hdr->flags & GRE_SEQ)
|
||||
nhoff += 4;
|
||||
if (proto == htons(ETH_P_TEB)) {
|
||||
const struct ethhdr *eth;
|
||||
struct ethhdr _eth;
|
||||
|
||||
eth = __skb_header_pointer(skb, nhoff,
|
||||
sizeof(_eth),
|
||||
data, hlen, &_eth);
|
||||
if (!eth)
|
||||
return false;
|
||||
proto = eth->h_proto;
|
||||
nhoff += sizeof(*eth);
|
||||
}
|
||||
goto again;
|
||||
}
|
||||
case NEXTHDR_HOP:
|
||||
case NEXTHDR_ROUTING:
|
||||
case NEXTHDR_DEST: {
|
||||
u8 _opthdr[2], *opthdr;
|
||||
|
||||
if (proto != htons(ETH_P_IPV6))
|
||||
break;
|
||||
|
||||
opthdr = __skb_header_pointer(skb, nhoff, sizeof(_opthdr),
|
||||
data, hlen, &_opthdr);
|
||||
if (!opthdr)
|
||||
return false;
|
||||
|
||||
ip_proto = opthdr[0];
|
||||
nhoff += (opthdr[1] + 1) << 3;
|
||||
|
||||
goto ip_proto_again;
|
||||
}
|
||||
case IPPROTO_IPIP:
|
||||
proto = htons(ETH_P_IP);
|
||||
@@ -244,18 +408,25 @@ bool __skb_flow_dissect(const struct sk_buff *skb, struct flow_keys *flow,
|
||||
case IPPROTO_IPV6:
|
||||
proto = htons(ETH_P_IPV6);
|
||||
goto ipv6;
|
||||
case IPPROTO_MPLS:
|
||||
proto = htons(ETH_P_MPLS_UC);
|
||||
goto mpls;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
flow->n_proto = proto;
|
||||
flow->ip_proto = ip_proto;
|
||||
flow->thoff = (u16) nhoff;
|
||||
key_basic->n_proto = proto;
|
||||
key_basic->ip_proto = ip_proto;
|
||||
key_control->thoff = (u16)nhoff;
|
||||
|
||||
/* unless skb is set we don't need to record port info */
|
||||
if (skb)
|
||||
flow->ports = __skb_flow_get_ports(skb, nhoff, ip_proto,
|
||||
data, hlen);
|
||||
if (skb_flow_dissector_uses_key(flow_dissector,
|
||||
FLOW_DISSECTOR_KEY_PORTS)) {
|
||||
key_ports = skb_flow_dissector_target(flow_dissector,
|
||||
FLOW_DISSECTOR_KEY_PORTS,
|
||||
target_container);
|
||||
key_ports->ports = __skb_flow_get_ports(skb, nhoff, ip_proto,
|
||||
data, hlen);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -267,27 +438,109 @@ static __always_inline void __flow_hash_secret_init(void)
|
||||
net_get_random_once(&hashrnd, sizeof(hashrnd));
|
||||
}
|
||||
|
||||
static __always_inline u32 __flow_hash_3words(u32 a, u32 b, u32 c)
|
||||
static __always_inline u32 __flow_hash_words(u32 *words, u32 length, u32 keyval)
|
||||
{
|
||||
__flow_hash_secret_init();
|
||||
return jhash_3words(a, b, c, hashrnd);
|
||||
return jhash2(words, length, keyval);
|
||||
}
|
||||
|
||||
static inline u32 __flow_hash_from_keys(struct flow_keys *keys)
|
||||
static inline void *flow_keys_hash_start(struct flow_keys *flow)
|
||||
{
|
||||
BUILD_BUG_ON(FLOW_KEYS_HASH_OFFSET % sizeof(u32));
|
||||
return (void *)flow + FLOW_KEYS_HASH_OFFSET;
|
||||
}
|
||||
|
||||
static inline size_t flow_keys_hash_length(struct flow_keys *flow)
|
||||
{
|
||||
size_t diff = FLOW_KEYS_HASH_OFFSET + sizeof(flow->addrs);
|
||||
BUILD_BUG_ON((sizeof(*flow) - FLOW_KEYS_HASH_OFFSET) % sizeof(u32));
|
||||
BUILD_BUG_ON(offsetof(typeof(*flow), addrs) !=
|
||||
sizeof(*flow) - sizeof(flow->addrs));
|
||||
|
||||
switch (flow->control.addr_type) {
|
||||
case FLOW_DISSECTOR_KEY_IPV4_ADDRS:
|
||||
diff -= sizeof(flow->addrs.v4addrs);
|
||||
break;
|
||||
case FLOW_DISSECTOR_KEY_IPV6_ADDRS:
|
||||
diff -= sizeof(flow->addrs.v6addrs);
|
||||
break;
|
||||
case FLOW_DISSECTOR_KEY_TIPC_ADDRS:
|
||||
diff -= sizeof(flow->addrs.tipcaddrs);
|
||||
break;
|
||||
}
|
||||
return (sizeof(*flow) - diff) / sizeof(u32);
|
||||
}
|
||||
|
||||
__be32 flow_get_u32_src(const struct flow_keys *flow)
|
||||
{
|
||||
switch (flow->control.addr_type) {
|
||||
case FLOW_DISSECTOR_KEY_IPV4_ADDRS:
|
||||
return flow->addrs.v4addrs.src;
|
||||
case FLOW_DISSECTOR_KEY_IPV6_ADDRS:
|
||||
return (__force __be32)ipv6_addr_hash(
|
||||
&flow->addrs.v6addrs.src);
|
||||
case FLOW_DISSECTOR_KEY_TIPC_ADDRS:
|
||||
return flow->addrs.tipcaddrs.srcnode;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL(flow_get_u32_src);
|
||||
|
||||
__be32 flow_get_u32_dst(const struct flow_keys *flow)
|
||||
{
|
||||
switch (flow->control.addr_type) {
|
||||
case FLOW_DISSECTOR_KEY_IPV4_ADDRS:
|
||||
return flow->addrs.v4addrs.dst;
|
||||
case FLOW_DISSECTOR_KEY_IPV6_ADDRS:
|
||||
return (__force __be32)ipv6_addr_hash(
|
||||
&flow->addrs.v6addrs.dst);
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL(flow_get_u32_dst);
|
||||
|
||||
static inline void __flow_hash_consistentify(struct flow_keys *keys)
|
||||
{
|
||||
int addr_diff, i;
|
||||
|
||||
switch (keys->control.addr_type) {
|
||||
case FLOW_DISSECTOR_KEY_IPV4_ADDRS:
|
||||
addr_diff = (__force u32)keys->addrs.v4addrs.dst -
|
||||
(__force u32)keys->addrs.v4addrs.src;
|
||||
if ((addr_diff < 0) ||
|
||||
(addr_diff == 0 &&
|
||||
((__force u16)keys->ports.dst <
|
||||
(__force u16)keys->ports.src))) {
|
||||
swap(keys->addrs.v4addrs.src, keys->addrs.v4addrs.dst);
|
||||
swap(keys->ports.src, keys->ports.dst);
|
||||
}
|
||||
break;
|
||||
case FLOW_DISSECTOR_KEY_IPV6_ADDRS:
|
||||
addr_diff = memcmp(&keys->addrs.v6addrs.dst,
|
||||
&keys->addrs.v6addrs.src,
|
||||
sizeof(keys->addrs.v6addrs.dst));
|
||||
if ((addr_diff < 0) ||
|
||||
(addr_diff == 0 &&
|
||||
((__force u16)keys->ports.dst <
|
||||
(__force u16)keys->ports.src))) {
|
||||
for (i = 0; i < 4; i++)
|
||||
swap(keys->addrs.v6addrs.src.s6_addr32[i],
|
||||
keys->addrs.v6addrs.dst.s6_addr32[i]);
|
||||
swap(keys->ports.src, keys->ports.dst);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static inline u32 __flow_hash_from_keys(struct flow_keys *keys, u32 keyval)
|
||||
{
|
||||
u32 hash;
|
||||
|
||||
/* get a consistent hash (same value on both flow directions) */
|
||||
if (((__force u32)keys->dst < (__force u32)keys->src) ||
|
||||
(((__force u32)keys->dst == (__force u32)keys->src) &&
|
||||
((__force u16)keys->port16[1] < (__force u16)keys->port16[0]))) {
|
||||
swap(keys->dst, keys->src);
|
||||
swap(keys->port16[0], keys->port16[1]);
|
||||
}
|
||||
__flow_hash_consistentify(keys);
|
||||
|
||||
hash = __flow_hash_3words((__force u32)keys->dst,
|
||||
(__force u32)keys->src,
|
||||
(__force u32)keys->ports);
|
||||
hash = __flow_hash_words((u32 *)flow_keys_hash_start(keys),
|
||||
flow_keys_hash_length(keys), keyval);
|
||||
if (!hash)
|
||||
hash = 1;
|
||||
|
||||
@@ -296,12 +549,52 @@ static inline u32 __flow_hash_from_keys(struct flow_keys *keys)
|
||||
|
||||
u32 flow_hash_from_keys(struct flow_keys *keys)
|
||||
{
|
||||
return __flow_hash_from_keys(keys);
|
||||
__flow_hash_secret_init();
|
||||
return __flow_hash_from_keys(keys, hashrnd);
|
||||
}
|
||||
EXPORT_SYMBOL(flow_hash_from_keys);
|
||||
|
||||
/*
|
||||
* __skb_get_hash: calculate a flow hash based on src/dst addresses
|
||||
static inline u32 ___skb_get_hash(const struct sk_buff *skb,
|
||||
struct flow_keys *keys, u32 keyval)
|
||||
{
|
||||
if (!skb_flow_dissect_flow_keys(skb, keys))
|
||||
return 0;
|
||||
|
||||
return __flow_hash_from_keys(keys, keyval);
|
||||
}
|
||||
|
||||
struct _flow_keys_digest_data {
|
||||
__be16 n_proto;
|
||||
u8 ip_proto;
|
||||
u8 padding;
|
||||
__be32 ports;
|
||||
__be32 src;
|
||||
__be32 dst;
|
||||
};
|
||||
|
||||
void make_flow_keys_digest(struct flow_keys_digest *digest,
|
||||
const struct flow_keys *flow)
|
||||
{
|
||||
struct _flow_keys_digest_data *data =
|
||||
(struct _flow_keys_digest_data *)digest;
|
||||
|
||||
BUILD_BUG_ON(sizeof(*data) > sizeof(*digest));
|
||||
|
||||
memset(digest, 0, sizeof(*digest));
|
||||
|
||||
data->n_proto = flow->basic.n_proto;
|
||||
data->ip_proto = flow->basic.ip_proto;
|
||||
data->ports = flow->ports.ports;
|
||||
data->src = flow->addrs.v4addrs.src;
|
||||
data->dst = flow->addrs.v4addrs.dst;
|
||||
}
|
||||
EXPORT_SYMBOL(make_flow_keys_digest);
|
||||
|
||||
/**
|
||||
* __skb_get_hash: calculate a flow hash
|
||||
* @skb: sk_buff to calculate flow hash from
|
||||
*
|
||||
* This function calculates a flow hash based on src/dst addresses
|
||||
* and src/dst port numbers. Sets hash in skb to non-zero hash value
|
||||
* on success, zero indicates no valid hash. Also, sets l4_hash in skb
|
||||
* if hash is a canonical 4-tuple hash over transport ports.
|
||||
@@ -309,53 +602,34 @@ EXPORT_SYMBOL(flow_hash_from_keys);
|
||||
void __skb_get_hash(struct sk_buff *skb)
|
||||
{
|
||||
struct flow_keys keys;
|
||||
u32 hash;
|
||||
|
||||
if (!skb_flow_dissect(skb, &keys))
|
||||
__flow_hash_secret_init();
|
||||
|
||||
hash = ___skb_get_hash(skb, &keys, hashrnd);
|
||||
if (!hash)
|
||||
return;
|
||||
|
||||
if (keys.ports)
|
||||
if (keys.ports.ports)
|
||||
skb->l4_hash = 1;
|
||||
|
||||
skb->sw_hash = 1;
|
||||
|
||||
skb->hash = __flow_hash_from_keys(&keys);
|
||||
skb->hash = hash;
|
||||
}
|
||||
EXPORT_SYMBOL(__skb_get_hash);
|
||||
|
||||
/*
|
||||
* Returns a Tx hash based on the given packet descriptor a Tx queues' number
|
||||
* to be used as a distribution range.
|
||||
*/
|
||||
u16 __skb_tx_hash(const struct net_device *dev, struct sk_buff *skb,
|
||||
unsigned int num_tx_queues)
|
||||
__u32 skb_get_hash_perturb(const struct sk_buff *skb, u32 perturb)
|
||||
{
|
||||
u32 hash;
|
||||
u16 qoffset = 0;
|
||||
u16 qcount = num_tx_queues;
|
||||
struct flow_keys keys;
|
||||
|
||||
if (skb_rx_queue_recorded(skb)) {
|
||||
hash = skb_get_rx_queue(skb);
|
||||
while (unlikely(hash >= num_tx_queues))
|
||||
hash -= num_tx_queues;
|
||||
return hash;
|
||||
}
|
||||
|
||||
if (dev->num_tc) {
|
||||
u8 tc = netdev_get_prio_tc_map(dev, skb->priority);
|
||||
qoffset = dev->tc_to_txq[tc].offset;
|
||||
qcount = dev->tc_to_txq[tc].count;
|
||||
}
|
||||
|
||||
return (u16) reciprocal_scale(skb_get_hash(skb), qcount) + qoffset;
|
||||
return ___skb_get_hash(skb, &keys, perturb);
|
||||
}
|
||||
EXPORT_SYMBOL(__skb_tx_hash);
|
||||
EXPORT_SYMBOL(skb_get_hash_perturb);
|
||||
|
||||
u32 __skb_get_poff(const struct sk_buff *skb, void *data,
|
||||
const struct flow_keys *keys, int hlen)
|
||||
{
|
||||
u32 poff = keys->thoff;
|
||||
u32 poff = keys->control.thoff;
|
||||
|
||||
switch (keys->ip_proto) {
|
||||
switch (keys->basic.ip_proto) {
|
||||
case IPPROTO_TCP: {
|
||||
/* access doff as u8 to avoid unaligned access */
|
||||
const u8 *doff;
|
||||
@@ -396,8 +670,12 @@ u32 __skb_get_poff(const struct sk_buff *skb, void *data,
|
||||
return poff;
|
||||
}
|
||||
|
||||
/* skb_get_poff() returns the offset to the payload as far as it could
|
||||
* be dissected. The main user is currently BPF, so that we can dynamically
|
||||
/**
|
||||
* skb_get_poff - get the offset to the payload
|
||||
* @skb: sk_buff to get the payload offset from
|
||||
*
|
||||
* The function will get the offset to the payload as far as it could
|
||||
* be dissected. The main user is currently BPF, so that we can dynamically
|
||||
* truncate packets without needing to push actual payload to the user
|
||||
* space and can analyze headers only, instead.
|
||||
*/
|
||||
@@ -405,86 +683,76 @@ u32 skb_get_poff(const struct sk_buff *skb)
|
||||
{
|
||||
struct flow_keys keys;
|
||||
|
||||
if (!skb_flow_dissect(skb, &keys))
|
||||
if (!skb_flow_dissect_flow_keys(skb, &keys))
|
||||
return 0;
|
||||
|
||||
return __skb_get_poff(skb, skb->data, &keys, skb_headlen(skb));
|
||||
}
|
||||
|
||||
static inline int get_xps_queue(struct net_device *dev, struct sk_buff *skb)
|
||||
static const struct flow_dissector_key flow_keys_dissector_keys[] = {
|
||||
{
|
||||
.key_id = FLOW_DISSECTOR_KEY_CONTROL,
|
||||
.offset = offsetof(struct flow_keys, control),
|
||||
},
|
||||
{
|
||||
.key_id = FLOW_DISSECTOR_KEY_BASIC,
|
||||
.offset = offsetof(struct flow_keys, basic),
|
||||
},
|
||||
{
|
||||
.key_id = FLOW_DISSECTOR_KEY_IPV4_ADDRS,
|
||||
.offset = offsetof(struct flow_keys, addrs.v4addrs),
|
||||
},
|
||||
{
|
||||
.key_id = FLOW_DISSECTOR_KEY_IPV6_ADDRS,
|
||||
.offset = offsetof(struct flow_keys, addrs.v6addrs),
|
||||
},
|
||||
{
|
||||
.key_id = FLOW_DISSECTOR_KEY_TIPC_ADDRS,
|
||||
.offset = offsetof(struct flow_keys, addrs.tipcaddrs),
|
||||
},
|
||||
{
|
||||
.key_id = FLOW_DISSECTOR_KEY_PORTS,
|
||||
.offset = offsetof(struct flow_keys, ports),
|
||||
},
|
||||
{
|
||||
.key_id = FLOW_DISSECTOR_KEY_VLANID,
|
||||
.offset = offsetof(struct flow_keys, tags),
|
||||
},
|
||||
{
|
||||
.key_id = FLOW_DISSECTOR_KEY_FLOW_LABEL,
|
||||
.offset = offsetof(struct flow_keys, tags),
|
||||
},
|
||||
{
|
||||
.key_id = FLOW_DISSECTOR_KEY_GRE_KEYID,
|
||||
.offset = offsetof(struct flow_keys, keyid),
|
||||
},
|
||||
};
|
||||
|
||||
static const struct flow_dissector_key flow_keys_buf_dissector_keys[] = {
|
||||
{
|
||||
.key_id = FLOW_DISSECTOR_KEY_CONTROL,
|
||||
.offset = offsetof(struct flow_keys, control),
|
||||
},
|
||||
{
|
||||
.key_id = FLOW_DISSECTOR_KEY_BASIC,
|
||||
.offset = offsetof(struct flow_keys, basic),
|
||||
},
|
||||
};
|
||||
|
||||
struct flow_dissector flow_keys_dissector __read_mostly;
|
||||
EXPORT_SYMBOL(flow_keys_dissector);
|
||||
|
||||
struct flow_dissector flow_keys_buf_dissector __read_mostly;
|
||||
|
||||
static int __init init_default_flow_dissectors(void)
|
||||
{
|
||||
#ifdef CONFIG_XPS
|
||||
struct xps_dev_maps *dev_maps;
|
||||
struct xps_map *map;
|
||||
int queue_index = -1;
|
||||
|
||||
rcu_read_lock();
|
||||
dev_maps = rcu_dereference(dev->xps_maps);
|
||||
if (dev_maps) {
|
||||
map = rcu_dereference(
|
||||
dev_maps->cpu_map[skb->sender_cpu - 1]);
|
||||
if (map) {
|
||||
if (map->len == 1)
|
||||
queue_index = map->queues[0];
|
||||
else
|
||||
queue_index = map->queues[reciprocal_scale(skb_get_hash(skb),
|
||||
map->len)];
|
||||
if (unlikely(queue_index >= dev->real_num_tx_queues))
|
||||
queue_index = -1;
|
||||
}
|
||||
}
|
||||
rcu_read_unlock();
|
||||
|
||||
return queue_index;
|
||||
#else
|
||||
return -1;
|
||||
#endif
|
||||
skb_flow_dissector_init(&flow_keys_dissector,
|
||||
flow_keys_dissector_keys,
|
||||
ARRAY_SIZE(flow_keys_dissector_keys));
|
||||
skb_flow_dissector_init(&flow_keys_buf_dissector,
|
||||
flow_keys_buf_dissector_keys,
|
||||
ARRAY_SIZE(flow_keys_buf_dissector_keys));
|
||||
return 0;
|
||||
}
|
||||
|
||||
static u16 __netdev_pick_tx(struct net_device *dev, struct sk_buff *skb)
|
||||
{
|
||||
struct sock *sk = skb->sk;
|
||||
int queue_index = sk_tx_queue_get(sk);
|
||||
|
||||
if (queue_index < 0 || skb->ooo_okay ||
|
||||
queue_index >= dev->real_num_tx_queues) {
|
||||
int new_index = get_xps_queue(dev, skb);
|
||||
if (new_index < 0)
|
||||
new_index = skb_tx_hash(dev, skb);
|
||||
|
||||
if (queue_index != new_index && sk &&
|
||||
rcu_access_pointer(sk->sk_dst_cache))
|
||||
sk_tx_queue_set(sk, new_index);
|
||||
|
||||
queue_index = new_index;
|
||||
}
|
||||
|
||||
return queue_index;
|
||||
}
|
||||
|
||||
struct netdev_queue *netdev_pick_tx(struct net_device *dev,
|
||||
struct sk_buff *skb,
|
||||
void *accel_priv)
|
||||
{
|
||||
int queue_index = 0;
|
||||
|
||||
#ifdef CONFIG_XPS
|
||||
if (skb->sender_cpu == 0)
|
||||
skb->sender_cpu = raw_smp_processor_id() + 1;
|
||||
#endif
|
||||
|
||||
if (dev->real_num_tx_queues != 1) {
|
||||
const struct net_device_ops *ops = dev->netdev_ops;
|
||||
if (ops->ndo_select_queue)
|
||||
queue_index = ops->ndo_select_queue(dev, skb, accel_priv,
|
||||
__netdev_pick_tx);
|
||||
else
|
||||
queue_index = __netdev_pick_tx(dev, skb);
|
||||
|
||||
if (!accel_priv)
|
||||
queue_index = netdev_cap_txqueue(dev, queue_index);
|
||||
}
|
||||
|
||||
skb_set_queue_mapping(skb, queue_index);
|
||||
return netdev_get_tx_queue(dev, queue_index);
|
||||
}
|
||||
late_initcall_sync(init_default_flow_dissectors);
|
||||
|
||||
@@ -913,6 +913,7 @@ static void neigh_timer_handler(unsigned long arg)
|
||||
neigh->nud_state = NUD_PROBE;
|
||||
neigh->updated = jiffies;
|
||||
atomic_set(&neigh->probes, 0);
|
||||
notify = 1;
|
||||
next = now + NEIGH_VAR(neigh->parms, RETRANS_TIME);
|
||||
}
|
||||
} else {
|
||||
@@ -957,6 +958,8 @@ int __neigh_event_send(struct neighbour *neigh, struct sk_buff *skb)
|
||||
rc = 0;
|
||||
if (neigh->nud_state & (NUD_CONNECTED | NUD_DELAY | NUD_PROBE))
|
||||
goto out_unlock_bh;
|
||||
if (neigh->dead)
|
||||
goto out_dead;
|
||||
|
||||
if (!(neigh->nud_state & (NUD_STALE | NUD_INCOMPLETE))) {
|
||||
if (NEIGH_VAR(neigh->parms, MCAST_PROBES) +
|
||||
@@ -1013,6 +1016,13 @@ int __neigh_event_send(struct neighbour *neigh, struct sk_buff *skb)
|
||||
write_unlock(&neigh->lock);
|
||||
local_bh_enable();
|
||||
return rc;
|
||||
|
||||
out_dead:
|
||||
if (neigh->nud_state & NUD_STALE)
|
||||
goto out_unlock_bh;
|
||||
write_unlock_bh(&neigh->lock);
|
||||
kfree_skb(skb);
|
||||
return 1;
|
||||
}
|
||||
EXPORT_SYMBOL(__neigh_event_send);
|
||||
|
||||
@@ -1076,6 +1086,8 @@ int neigh_update(struct neighbour *neigh, const u8 *lladdr, u8 new,
|
||||
if (!(flags & NEIGH_UPDATE_F_ADMIN) &&
|
||||
(old & (NUD_NOARP | NUD_PERMANENT)))
|
||||
goto out;
|
||||
if (neigh->dead)
|
||||
goto out;
|
||||
|
||||
if (!(new & NUD_VALID)) {
|
||||
neigh_del_timer(neigh);
|
||||
@@ -1144,6 +1156,8 @@ int neigh_update(struct neighbour *neigh, const u8 *lladdr, u8 new,
|
||||
|
||||
if (new != old) {
|
||||
neigh_del_timer(neigh);
|
||||
if (new & NUD_PROBE)
|
||||
atomic_set(&neigh->probes, 0);
|
||||
if (new & NUD_IN_TIMER)
|
||||
neigh_add_timer(neigh, (jiffies +
|
||||
((new & NUD_REACHABLE) ?
|
||||
@@ -1225,6 +1239,8 @@ EXPORT_SYMBOL(neigh_update);
|
||||
*/
|
||||
void __neigh_set_probe_once(struct neighbour *neigh)
|
||||
{
|
||||
if (neigh->dead)
|
||||
return;
|
||||
neigh->updated = jiffies;
|
||||
if (!(neigh->nud_state & NUD_FAILED))
|
||||
return;
|
||||
|
||||
@@ -458,11 +458,15 @@ static ssize_t phys_switch_id_show(struct device *dev,
|
||||
return restart_syscall();
|
||||
|
||||
if (dev_isalive(netdev)) {
|
||||
struct netdev_phys_item_id ppid;
|
||||
struct switchdev_attr attr = {
|
||||
.id = SWITCHDEV_ATTR_PORT_PARENT_ID,
|
||||
.flags = SWITCHDEV_F_NO_RECURSE,
|
||||
};
|
||||
|
||||
ret = netdev_switch_parent_id_get(netdev, &ppid);
|
||||
ret = switchdev_port_attr_get(netdev, &attr);
|
||||
if (!ret)
|
||||
ret = sprintf(buf, "%*phN\n", ppid.id_len, ppid.id);
|
||||
ret = sprintf(buf, "%*phN\n", attr.u.ppid.id_len,
|
||||
attr.u.ppid.id);
|
||||
}
|
||||
rtnl_unlock();
|
||||
|
||||
|
||||
@@ -147,24 +147,17 @@ static void ops_free_list(const struct pernet_operations *ops,
|
||||
}
|
||||
}
|
||||
|
||||
static void rtnl_net_notifyid(struct net *net, struct net *peer, int cmd,
|
||||
int id);
|
||||
/* should be called with nsid_lock held */
|
||||
static int alloc_netid(struct net *net, struct net *peer, int reqid)
|
||||
{
|
||||
int min = 0, max = 0, id;
|
||||
|
||||
ASSERT_RTNL();
|
||||
int min = 0, max = 0;
|
||||
|
||||
if (reqid >= 0) {
|
||||
min = reqid;
|
||||
max = reqid + 1;
|
||||
}
|
||||
|
||||
id = idr_alloc(&net->netns_ids, peer, min, max, GFP_KERNEL);
|
||||
if (id >= 0)
|
||||
rtnl_net_notifyid(net, peer, RTM_NEWNSID, id);
|
||||
|
||||
return id;
|
||||
return idr_alloc(&net->netns_ids, peer, min, max, GFP_ATOMIC);
|
||||
}
|
||||
|
||||
/* This function is used by idr_for_each(). If net is equal to peer, the
|
||||
@@ -180,11 +173,16 @@ static int net_eq_idr(int id, void *net, void *peer)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __peernet2id(struct net *net, struct net *peer, bool alloc)
|
||||
/* Should be called with nsid_lock held. If a new id is assigned, the bool alloc
|
||||
* is set to true, thus the caller knows that the new id must be notified via
|
||||
* rtnl.
|
||||
*/
|
||||
static int __peernet2id_alloc(struct net *net, struct net *peer, bool *alloc)
|
||||
{
|
||||
int id = idr_for_each(&net->netns_ids, net_eq_idr, peer);
|
||||
bool alloc_it = *alloc;
|
||||
|
||||
ASSERT_RTNL();
|
||||
*alloc = false;
|
||||
|
||||
/* Magic value for id 0. */
|
||||
if (id == NET_ID_ZERO)
|
||||
@@ -192,36 +190,77 @@ static int __peernet2id(struct net *net, struct net *peer, bool alloc)
|
||||
if (id > 0)
|
||||
return id;
|
||||
|
||||
if (alloc)
|
||||
return alloc_netid(net, peer, -1);
|
||||
if (alloc_it) {
|
||||
id = alloc_netid(net, peer, -1);
|
||||
*alloc = true;
|
||||
return id >= 0 ? id : NETNSA_NSID_NOT_ASSIGNED;
|
||||
}
|
||||
|
||||
return -ENOENT;
|
||||
return NETNSA_NSID_NOT_ASSIGNED;
|
||||
}
|
||||
|
||||
/* should be called with nsid_lock held */
|
||||
static int __peernet2id(struct net *net, struct net *peer)
|
||||
{
|
||||
bool no = false;
|
||||
|
||||
return __peernet2id_alloc(net, peer, &no);
|
||||
}
|
||||
|
||||
static void rtnl_net_notifyid(struct net *net, int cmd, int id);
|
||||
/* This function returns the id of a peer netns. If no id is assigned, one will
|
||||
* be allocated and returned.
|
||||
*/
|
||||
int peernet2id(struct net *net, struct net *peer)
|
||||
int peernet2id_alloc(struct net *net, struct net *peer)
|
||||
{
|
||||
bool alloc = atomic_read(&peer->count) == 0 ? false : true;
|
||||
unsigned long flags;
|
||||
bool alloc;
|
||||
int id;
|
||||
|
||||
id = __peernet2id(net, peer, alloc);
|
||||
return id >= 0 ? id : NETNSA_NSID_NOT_ASSIGNED;
|
||||
spin_lock_irqsave(&net->nsid_lock, flags);
|
||||
alloc = atomic_read(&peer->count) == 0 ? false : true;
|
||||
id = __peernet2id_alloc(net, peer, &alloc);
|
||||
spin_unlock_irqrestore(&net->nsid_lock, flags);
|
||||
if (alloc && id >= 0)
|
||||
rtnl_net_notifyid(net, RTM_NEWNSID, id);
|
||||
return id;
|
||||
}
|
||||
EXPORT_SYMBOL(peernet2id_alloc);
|
||||
|
||||
/* This function returns, if assigned, the id of a peer netns. */
|
||||
int peernet2id(struct net *net, struct net *peer)
|
||||
{
|
||||
unsigned long flags;
|
||||
int id;
|
||||
|
||||
spin_lock_irqsave(&net->nsid_lock, flags);
|
||||
id = __peernet2id(net, peer);
|
||||
spin_unlock_irqrestore(&net->nsid_lock, flags);
|
||||
return id;
|
||||
}
|
||||
|
||||
/* This function returns true is the peer netns has an id assigned into the
|
||||
* current netns.
|
||||
*/
|
||||
bool peernet_has_id(struct net *net, struct net *peer)
|
||||
{
|
||||
return peernet2id(net, peer) >= 0;
|
||||
}
|
||||
EXPORT_SYMBOL(peernet2id);
|
||||
|
||||
struct net *get_net_ns_by_id(struct net *net, int id)
|
||||
{
|
||||
unsigned long flags;
|
||||
struct net *peer;
|
||||
|
||||
if (id < 0)
|
||||
return NULL;
|
||||
|
||||
rcu_read_lock();
|
||||
spin_lock_irqsave(&net->nsid_lock, flags);
|
||||
peer = idr_find(&net->netns_ids, id);
|
||||
if (peer)
|
||||
get_net(peer);
|
||||
spin_unlock_irqrestore(&net->nsid_lock, flags);
|
||||
rcu_read_unlock();
|
||||
|
||||
return peer;
|
||||
@@ -242,6 +281,7 @@ static __net_init int setup_net(struct net *net, struct user_namespace *user_ns)
|
||||
net->dev_base_seq = 1;
|
||||
net->user_ns = user_ns;
|
||||
idr_init(&net->netns_ids);
|
||||
spin_lock_init(&net->nsid_lock);
|
||||
|
||||
list_for_each_entry(ops, &pernet_list, list) {
|
||||
error = ops_init(ops, net);
|
||||
@@ -362,14 +402,19 @@ static void cleanup_net(struct work_struct *work)
|
||||
list_del_rcu(&net->list);
|
||||
list_add_tail(&net->exit_list, &net_exit_list);
|
||||
for_each_net(tmp) {
|
||||
int id = __peernet2id(tmp, net, false);
|
||||
int id;
|
||||
|
||||
if (id >= 0) {
|
||||
rtnl_net_notifyid(tmp, net, RTM_DELNSID, id);
|
||||
spin_lock_irq(&tmp->nsid_lock);
|
||||
id = __peernet2id(tmp, net);
|
||||
if (id >= 0)
|
||||
idr_remove(&tmp->netns_ids, id);
|
||||
}
|
||||
spin_unlock_irq(&tmp->nsid_lock);
|
||||
if (id >= 0)
|
||||
rtnl_net_notifyid(tmp, RTM_DELNSID, id);
|
||||
}
|
||||
spin_lock_irq(&net->nsid_lock);
|
||||
idr_destroy(&net->netns_ids);
|
||||
spin_unlock_irq(&net->nsid_lock);
|
||||
|
||||
}
|
||||
rtnl_unlock();
|
||||
@@ -497,6 +542,7 @@ static int rtnl_net_newid(struct sk_buff *skb, struct nlmsghdr *nlh)
|
||||
{
|
||||
struct net *net = sock_net(skb->sk);
|
||||
struct nlattr *tb[NETNSA_MAX + 1];
|
||||
unsigned long flags;
|
||||
struct net *peer;
|
||||
int nsid, err;
|
||||
|
||||
@@ -517,14 +563,19 @@ static int rtnl_net_newid(struct sk_buff *skb, struct nlmsghdr *nlh)
|
||||
if (IS_ERR(peer))
|
||||
return PTR_ERR(peer);
|
||||
|
||||
if (__peernet2id(net, peer, false) >= 0) {
|
||||
spin_lock_irqsave(&net->nsid_lock, flags);
|
||||
if (__peernet2id(net, peer) >= 0) {
|
||||
spin_unlock_irqrestore(&net->nsid_lock, flags);
|
||||
err = -EEXIST;
|
||||
goto out;
|
||||
}
|
||||
|
||||
err = alloc_netid(net, peer, nsid);
|
||||
if (err > 0)
|
||||
spin_unlock_irqrestore(&net->nsid_lock, flags);
|
||||
if (err >= 0) {
|
||||
rtnl_net_notifyid(net, RTM_NEWNSID, err);
|
||||
err = 0;
|
||||
}
|
||||
out:
|
||||
put_net(peer);
|
||||
return err;
|
||||
@@ -538,14 +589,10 @@ static int rtnl_net_get_size(void)
|
||||
}
|
||||
|
||||
static int rtnl_net_fill(struct sk_buff *skb, u32 portid, u32 seq, int flags,
|
||||
int cmd, struct net *net, struct net *peer,
|
||||
int nsid)
|
||||
int cmd, struct net *net, int nsid)
|
||||
{
|
||||
struct nlmsghdr *nlh;
|
||||
struct rtgenmsg *rth;
|
||||
int id;
|
||||
|
||||
ASSERT_RTNL();
|
||||
|
||||
nlh = nlmsg_put(skb, portid, seq, cmd, sizeof(*rth), flags);
|
||||
if (!nlh)
|
||||
@@ -554,14 +601,7 @@ static int rtnl_net_fill(struct sk_buff *skb, u32 portid, u32 seq, int flags,
|
||||
rth = nlmsg_data(nlh);
|
||||
rth->rtgen_family = AF_UNSPEC;
|
||||
|
||||
if (nsid >= 0) {
|
||||
id = nsid;
|
||||
} else {
|
||||
id = __peernet2id(net, peer, false);
|
||||
if (id < 0)
|
||||
id = NETNSA_NSID_NOT_ASSIGNED;
|
||||
}
|
||||
if (nla_put_s32(skb, NETNSA_NSID, id))
|
||||
if (nla_put_s32(skb, NETNSA_NSID, nsid))
|
||||
goto nla_put_failure;
|
||||
|
||||
nlmsg_end(skb, nlh);
|
||||
@@ -578,7 +618,7 @@ static int rtnl_net_getid(struct sk_buff *skb, struct nlmsghdr *nlh)
|
||||
struct nlattr *tb[NETNSA_MAX + 1];
|
||||
struct sk_buff *msg;
|
||||
struct net *peer;
|
||||
int err;
|
||||
int err, id;
|
||||
|
||||
err = nlmsg_parse(nlh, sizeof(struct rtgenmsg), tb, NETNSA_MAX,
|
||||
rtnl_net_policy);
|
||||
@@ -600,8 +640,9 @@ static int rtnl_net_getid(struct sk_buff *skb, struct nlmsghdr *nlh)
|
||||
goto out;
|
||||
}
|
||||
|
||||
id = peernet2id(net, peer);
|
||||
err = rtnl_net_fill(msg, NETLINK_CB(skb).portid, nlh->nlmsg_seq, 0,
|
||||
RTM_NEWNSID, net, peer, -1);
|
||||
RTM_NEWNSID, net, id);
|
||||
if (err < 0)
|
||||
goto err_out;
|
||||
|
||||
@@ -633,7 +674,7 @@ static int rtnl_net_dumpid_one(int id, void *peer, void *data)
|
||||
|
||||
ret = rtnl_net_fill(net_cb->skb, NETLINK_CB(net_cb->cb->skb).portid,
|
||||
net_cb->cb->nlh->nlmsg_seq, NLM_F_MULTI,
|
||||
RTM_NEWNSID, net_cb->net, peer, id);
|
||||
RTM_NEWNSID, net_cb->net, id);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
@@ -652,17 +693,17 @@ static int rtnl_net_dumpid(struct sk_buff *skb, struct netlink_callback *cb)
|
||||
.idx = 0,
|
||||
.s_idx = cb->args[0],
|
||||
};
|
||||
unsigned long flags;
|
||||
|
||||
ASSERT_RTNL();
|
||||
|
||||
spin_lock_irqsave(&net->nsid_lock, flags);
|
||||
idr_for_each(&net->netns_ids, rtnl_net_dumpid_one, &net_cb);
|
||||
spin_unlock_irqrestore(&net->nsid_lock, flags);
|
||||
|
||||
cb->args[0] = net_cb.idx;
|
||||
return skb->len;
|
||||
}
|
||||
|
||||
static void rtnl_net_notifyid(struct net *net, struct net *peer, int cmd,
|
||||
int id)
|
||||
static void rtnl_net_notifyid(struct net *net, int cmd, int id)
|
||||
{
|
||||
struct sk_buff *msg;
|
||||
int err = -ENOMEM;
|
||||
@@ -671,7 +712,7 @@ static void rtnl_net_notifyid(struct net *net, struct net *peer, int cmd,
|
||||
if (!msg)
|
||||
goto out;
|
||||
|
||||
err = rtnl_net_fill(msg, 0, 0, 0, cmd, net, peer, id);
|
||||
err = rtnl_net_fill(msg, 0, 0, 0, cmd, net, id);
|
||||
if (err < 0)
|
||||
goto err_out;
|
||||
|
||||
|
||||
@@ -31,10 +31,7 @@ static ATOMIC_NOTIFIER_HEAD(netevent_notif_chain);
|
||||
*/
|
||||
int register_netevent_notifier(struct notifier_block *nb)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = atomic_notifier_chain_register(&netevent_notif_chain, nb);
|
||||
return err;
|
||||
return atomic_notifier_chain_register(&netevent_notif_chain, nb);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(register_netevent_notifier);
|
||||
|
||||
|
||||
@@ -177,7 +177,7 @@
|
||||
#include <asm/dma.h>
|
||||
#include <asm/div64.h> /* do_div */
|
||||
|
||||
#define VERSION "2.74"
|
||||
#define VERSION "2.75"
|
||||
#define IP_NAME_SZ 32
|
||||
#define MAX_MPLS_LABELS 16 /* This is the max label stack depth */
|
||||
#define MPLS_STACK_BOTTOM htonl(0x00000100)
|
||||
@@ -210,6 +210,10 @@
|
||||
#define T_REMDEVALL (1<<2) /* Remove all devs */
|
||||
#define T_REMDEV (1<<3) /* Remove one dev */
|
||||
|
||||
/* Xmit modes */
|
||||
#define M_START_XMIT 0 /* Default normal TX */
|
||||
#define M_NETIF_RECEIVE 1 /* Inject packets into stack */
|
||||
|
||||
/* If lock -- protects updating of if_list */
|
||||
#define if_lock(t) spin_lock(&(t->if_lock));
|
||||
#define if_unlock(t) spin_unlock(&(t->if_lock));
|
||||
@@ -251,13 +255,14 @@ struct pktgen_dev {
|
||||
* we will do a random selection from within the range.
|
||||
*/
|
||||
__u32 flags;
|
||||
int removal_mark; /* non-zero => the device is marked for
|
||||
* removal by worker thread */
|
||||
|
||||
int xmit_mode;
|
||||
int min_pkt_size;
|
||||
int max_pkt_size;
|
||||
int pkt_overhead; /* overhead for MPLS, VLANs, IPSEC etc */
|
||||
int nfrags;
|
||||
int removal_mark; /* non-zero => the device is marked for
|
||||
* removal by worker thread */
|
||||
|
||||
struct page *page;
|
||||
u64 delay; /* nano-seconds */
|
||||
|
||||
@@ -507,7 +512,7 @@ static ssize_t pgctrl_write(struct file *file, const char __user *buf,
|
||||
pktgen_reset_all_threads(pn);
|
||||
|
||||
else
|
||||
pr_warn("Unknown command: %s\n", data);
|
||||
return -EINVAL;
|
||||
|
||||
return count;
|
||||
}
|
||||
@@ -567,7 +572,7 @@ static int pktgen_if_show(struct seq_file *seq, void *v)
|
||||
" dst_min: %s dst_max: %s\n",
|
||||
pkt_dev->dst_min, pkt_dev->dst_max);
|
||||
seq_printf(seq,
|
||||
" src_min: %s src_max: %s\n",
|
||||
" src_min: %s src_max: %s\n",
|
||||
pkt_dev->src_min, pkt_dev->src_max);
|
||||
}
|
||||
|
||||
@@ -620,6 +625,9 @@ static int pktgen_if_show(struct seq_file *seq, void *v)
|
||||
if (pkt_dev->node >= 0)
|
||||
seq_printf(seq, " node: %d\n", pkt_dev->node);
|
||||
|
||||
if (pkt_dev->xmit_mode == M_NETIF_RECEIVE)
|
||||
seq_puts(seq, " xmit_mode: netif_receive\n");
|
||||
|
||||
seq_puts(seq, " Flags: ");
|
||||
|
||||
if (pkt_dev->flags & F_IPV6)
|
||||
@@ -1081,7 +1089,8 @@ static ssize_t pktgen_if_write(struct file *file,
|
||||
if (len < 0)
|
||||
return len;
|
||||
if ((value > 0) &&
|
||||
(!(pkt_dev->odev->priv_flags & IFF_TX_SKB_SHARING)))
|
||||
((pkt_dev->xmit_mode == M_NETIF_RECEIVE) ||
|
||||
!(pkt_dev->odev->priv_flags & IFF_TX_SKB_SHARING)))
|
||||
return -ENOTSUPP;
|
||||
i += len;
|
||||
pkt_dev->clone_skb = value;
|
||||
@@ -1134,7 +1143,7 @@ static ssize_t pktgen_if_write(struct file *file,
|
||||
return len;
|
||||
|
||||
i += len;
|
||||
if ((value > 1) &&
|
||||
if ((value > 1) && (pkt_dev->xmit_mode == M_START_XMIT) &&
|
||||
(!(pkt_dev->odev->priv_flags & IFF_TX_SKB_SHARING)))
|
||||
return -ENOTSUPP;
|
||||
pkt_dev->burst = value < 1 ? 1 : value;
|
||||
@@ -1160,6 +1169,45 @@ static ssize_t pktgen_if_write(struct file *file,
|
||||
sprintf(pg_result, "ERROR: node not possible");
|
||||
return count;
|
||||
}
|
||||
if (!strcmp(name, "xmit_mode")) {
|
||||
char f[32];
|
||||
|
||||
memset(f, 0, 32);
|
||||
len = strn_len(&user_buffer[i], sizeof(f) - 1);
|
||||
if (len < 0)
|
||||
return len;
|
||||
|
||||
if (copy_from_user(f, &user_buffer[i], len))
|
||||
return -EFAULT;
|
||||
i += len;
|
||||
|
||||
if (strcmp(f, "start_xmit") == 0) {
|
||||
pkt_dev->xmit_mode = M_START_XMIT;
|
||||
} else if (strcmp(f, "netif_receive") == 0) {
|
||||
/* clone_skb set earlier, not supported in this mode */
|
||||
if (pkt_dev->clone_skb > 0)
|
||||
return -ENOTSUPP;
|
||||
|
||||
pkt_dev->xmit_mode = M_NETIF_RECEIVE;
|
||||
|
||||
/* make sure new packet is allocated every time
|
||||
* pktgen_xmit() is called
|
||||
*/
|
||||
pkt_dev->last_ok = 1;
|
||||
|
||||
/* override clone_skb if user passed default value
|
||||
* at module loading time
|
||||
*/
|
||||
pkt_dev->clone_skb = 0;
|
||||
} else {
|
||||
sprintf(pg_result,
|
||||
"xmit_mode -:%s:- unknown\nAvailable modes: %s",
|
||||
f, "start_xmit, netif_receive\n");
|
||||
return count;
|
||||
}
|
||||
sprintf(pg_result, "OK: xmit_mode=%s", f);
|
||||
return count;
|
||||
}
|
||||
if (!strcmp(name, "flag")) {
|
||||
char f[32];
|
||||
memset(f, 0, 32);
|
||||
@@ -1267,6 +1315,9 @@ static ssize_t pktgen_if_write(struct file *file,
|
||||
else if (strcmp(f, "NO_TIMESTAMP") == 0)
|
||||
pkt_dev->flags |= F_NO_TIMESTAMP;
|
||||
|
||||
else if (strcmp(f, "!NO_TIMESTAMP") == 0)
|
||||
pkt_dev->flags &= ~F_NO_TIMESTAMP;
|
||||
|
||||
else {
|
||||
sprintf(pg_result,
|
||||
"Flag -:%s:- unknown\nAvailable flags, (prepend ! to un-set flag):\n%s",
|
||||
@@ -2592,9 +2643,9 @@ static int process_ipsec(struct pktgen_dev *pkt_dev,
|
||||
struct xfrm_state *x = pkt_dev->flows[pkt_dev->curfl].x;
|
||||
int nhead = 0;
|
||||
if (x) {
|
||||
int ret;
|
||||
__u8 *eth;
|
||||
struct ethhdr *eth;
|
||||
struct iphdr *iph;
|
||||
int ret;
|
||||
|
||||
nhead = x->props.header_len - skb_headroom(skb);
|
||||
if (nhead > 0) {
|
||||
@@ -2614,9 +2665,9 @@ static int process_ipsec(struct pktgen_dev *pkt_dev,
|
||||
goto err;
|
||||
}
|
||||
/* restore ll */
|
||||
eth = (__u8 *) skb_push(skb, ETH_HLEN);
|
||||
memcpy(eth, pkt_dev->hh, 12);
|
||||
*(u16 *) ð[12] = protocol;
|
||||
eth = (struct ethhdr *)skb_push(skb, ETH_HLEN);
|
||||
memcpy(eth, pkt_dev->hh, 2 * ETH_ALEN);
|
||||
eth->h_proto = protocol;
|
||||
|
||||
/* Update IPv4 header len as well as checksum value */
|
||||
iph = ip_hdr(skb);
|
||||
@@ -3315,6 +3366,7 @@ static void pktgen_xmit(struct pktgen_dev *pkt_dev)
|
||||
unsigned int burst = ACCESS_ONCE(pkt_dev->burst);
|
||||
struct net_device *odev = pkt_dev->odev;
|
||||
struct netdev_queue *txq;
|
||||
struct sk_buff *skb;
|
||||
int ret;
|
||||
|
||||
/* If device is offline, then don't send */
|
||||
@@ -3352,6 +3404,37 @@ static void pktgen_xmit(struct pktgen_dev *pkt_dev)
|
||||
if (pkt_dev->delay && pkt_dev->last_ok)
|
||||
spin(pkt_dev, pkt_dev->next_tx);
|
||||
|
||||
if (pkt_dev->xmit_mode == M_NETIF_RECEIVE) {
|
||||
skb = pkt_dev->skb;
|
||||
skb->protocol = eth_type_trans(skb, skb->dev);
|
||||
atomic_add(burst, &skb->users);
|
||||
local_bh_disable();
|
||||
do {
|
||||
ret = netif_receive_skb(skb);
|
||||
if (ret == NET_RX_DROP)
|
||||
pkt_dev->errors++;
|
||||
pkt_dev->sofar++;
|
||||
pkt_dev->seq_num++;
|
||||
if (atomic_read(&skb->users) != burst) {
|
||||
/* skb was queued by rps/rfs or taps,
|
||||
* so cannot reuse this skb
|
||||
*/
|
||||
atomic_sub(burst - 1, &skb->users);
|
||||
/* get out of the loop and wait
|
||||
* until skb is consumed
|
||||
*/
|
||||
break;
|
||||
}
|
||||
/* skb was 'freed' by stack, so clean few
|
||||
* bits and reuse it
|
||||
*/
|
||||
#ifdef CONFIG_NET_CLS_ACT
|
||||
skb->tc_verd = 0; /* reset reclass/redir ttl */
|
||||
#endif
|
||||
} while (--burst > 0);
|
||||
goto out; /* Skips xmit_mode M_START_XMIT */
|
||||
}
|
||||
|
||||
txq = skb_get_tx_queue(odev, pkt_dev->skb);
|
||||
|
||||
local_bh_disable();
|
||||
@@ -3399,6 +3482,7 @@ static void pktgen_xmit(struct pktgen_dev *pkt_dev)
|
||||
unlock:
|
||||
HARD_TX_UNLOCK(odev, txq);
|
||||
|
||||
out:
|
||||
local_bh_enable();
|
||||
|
||||
/* If pkt_dev->count is zero, then run forever */
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user