summaryrefslogtreecommitdiff
path: root/net/bridge/br_multicast_eht.c
blob: 5cebca45e72c1f27de0399da571510967896d7f6 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
// SPDX-License-Identifier: GPL-2.0-or-later
// Copyright (c) 2020, Nikolay Aleksandrov <nikolay@nvidia.com>
#include <linux/err.h>
#include <linux/export.h>
#include <linux/if_ether.h>
#include <linux/igmp.h>
#include <linux/in.h>
#include <linux/jhash.h>
#include <linux/kernel.h>
#include <linux/log2.h>
#include <linux/netdevice.h>
#include <linux/netfilter_bridge.h>
#include <linux/random.h>
#include <linux/rculist.h>
#include <linux/skbuff.h>
#include <linux/slab.h>
#include <linux/timer.h>
#include <linux/inetdevice.h>
#include <linux/mroute.h>
#include <net/ip.h>
#include <net/switchdev.h>
#if IS_ENABLED(CONFIG_IPV6)
#include <linux/icmpv6.h>
#include <net/ipv6.h>
#include <net/mld.h>
#include <net/ip6_checksum.h>
#include <net/addrconf.h>
#endif

#include "br_private.h"
#include "br_private_mcast_eht.h"

static struct net_bridge_group_eht_host *
br_multicast_eht_host_lookup(struct net_bridge_port_group *pg,
			     union net_bridge_eht_addr *h_addr)
{
	struct rb_node *node = pg->eht_host_tree.rb_node;

	while (node) {
		struct net_bridge_group_eht_host *this;
		int result;

		this = rb_entry(node, struct net_bridge_group_eht_host,
				rb_node);
		result = memcmp(h_addr, &this->h_addr, sizeof(*h_addr));
		if (result < 0)
			node = node->rb_left;
		else if (result > 0)
			node = node->rb_right;
		else
			return this;
	}

	return NULL;
}

static int br_multicast_eht_host_filter_mode(struct net_bridge_port_group *pg,
					     union net_bridge_eht_addr *h_addr)
{
	struct net_bridge_group_eht_host *eht_host;

	eht_host = br_multicast_eht_host_lookup(pg, h_addr);
	if (!eht_host)
		return MCAST_INCLUDE;

	return eht_host->filter_mode;
}

static void __eht_destroy_host(struct net_bridge_group_eht_host *eht_host)
{
	WARN_ON(!hlist_empty(&eht_host->set_entries));

	rb_erase(&eht_host->rb_node, &eht_host->pg->eht_host_tree);
	RB_CLEAR_NODE(&eht_host->rb_node);
	kfree(eht_host);
}

static struct net_bridge_group_eht_host *
__eht_lookup_create_host(struct net_bridge_port_group *pg,
			 union net_bridge_eht_addr *h_addr,
			 unsigned char filter_mode)
{
	struct rb_node **link = &pg->eht_host_tree.rb_node, *parent = NULL;
	struct net_bridge_group_eht_host *eht_host;

	while (*link) {
		struct net_bridge_group_eht_host *this;
		int result;

		this = rb_entry(*link, struct net_bridge_group_eht_host,
				rb_node);
		result = memcmp(h_addr, &this->h_addr, sizeof(*h_addr));
		parent = *link;
		if (result < 0)
			link = &((*link)->rb_left);
		else if (result > 0)
			link = &((*link)->rb_right);
		else
			return this;
	}

	eht_host = kzalloc(sizeof(*eht_host), GFP_ATOMIC);
	if (!eht_host)
		return NULL;

	memcpy(&eht_host->h_addr, h_addr, sizeof(*h_addr));
	INIT_HLIST_HEAD(&eht_host->set_entries);
	eht_host->pg = pg;
	eht_host->filter_mode = filter_mode;

	rb_link_node(&eht_host->rb_node, parent, link);
	rb_insert_color(&eht_host->rb_node, &pg->eht_host_tree);

	return eht_host;
}