blob: d0764d239a55c91c1b5f6661a78ae67c24247fe4 [file] [log] [blame]
developer8efbb8e2022-10-17 22:55:12 +08001diff --git a/net/openvswitch/actions.c b/net/openvswitch/actions.c
2index 9e8a5c4..16f5187 100644
3--- a/net/openvswitch/actions.c
4+++ b/net/openvswitch/actions.c
5@@ -919,6 +919,10 @@ static void do_output(struct datapath *dp, struct sk_buff *skb, int out_port,
6 struct sw_flow_key *key)
7 {
8 struct vport *vport = ovs_vport_rcu(dp, out_port);
9+ struct multicast_data_base *mdb;
10+ struct multicast_table *table;
11+ struct multicast_table_entry *entry;
12+ struct sk_buff *skb_cpy;
13
14 if (likely(vport)) {
15 u16 mru = OVS_CB(skb)->mru;
16@@ -933,7 +937,28 @@ static void do_output(struct datapath *dp, struct sk_buff *skb, int out_port,
17
18 if (likely(!mru ||
19 (skb->len <= mru + vport->dev->hard_header_len))) {
20- ovs_vport_send(vport, skb, ovs_key_mac_proto(key));
21+ if (is_ipv4_multicast(skb) && !is_igmp(skb)) {
22+ mdb = vport->mdb;
23+ spin_lock(&mdb->tbl_lock);
24+ list_for_each_entry(table, &mdb->list_head, mdb_node) {
25+ if (table->group_addr.u.ip4 == key->ipv4.addr.dst) {
26+ list_for_each_entry(entry, &table->entry_list, entry_node) {
27+ skb_cpy = skb_copy(skb, GFP_ATOMIC);
28+ if (!skb_cpy) {
29+ kfree_skb(skb);
30+ pr_err("%s(): error\n", __func__);
31+ spin_unlock(&mdb->tbl_lock);
32+ return;
33+ }
34+ memcpy(skb_cpy->data, entry->eth_addr, ETH_ALEN);
35+ ovs_vport_send(vport, skb_cpy, ovs_key_mac_proto(key));
36+ }
37+ }
38+ }
39+ spin_unlock(&mdb->tbl_lock);
40+ kfree_skb(skb);
41+ } else
42+ ovs_vport_send(vport, skb, ovs_key_mac_proto(key));
43 } else if (mru <= vport->dev->mtu) {
44 struct net *net = read_pnet(&dp->net);
45
46diff --git a/net/openvswitch/datapath.c b/net/openvswitch/datapath.c
47index 4f097bd..e6be550 100644
48--- a/net/openvswitch/datapath.c
49+++ b/net/openvswitch/datapath.c
50@@ -11,6 +11,7 @@
51 #include <linux/if_vlan.h>
52 #include <linux/in.h>
53 #include <linux/ip.h>
54+#include <linux/igmp.h>
55 #include <linux/jhash.h>
56 #include <linux/delay.h>
57 #include <linux/time.h>
58@@ -530,6 +531,166 @@ static int queue_userspace_packet(struct datapath *dp, struct sk_buff *skb,
59 return err;
60 }
61
62+static int ovs_ip4_multicast_add_group(__be32 _group_addr,
63+ const u8 *entry_addr,
64+ struct vport *input_vport)
65+{
66+ struct multicast_data_base *mdb;
67+ struct multicast_table *table;
68+ struct multicast_table_entry *entry;
69+
70+ if (ipv4_is_local_multicast(_group_addr))
71+ return 0;
72+
73+ mdb = input_vport->mdb;
74+ spin_lock(&mdb->tbl_lock);
75+ list_for_each_entry(table, &mdb->list_head, mdb_node) {
76+ if (table->group_addr.u.ip4 == _group_addr) {
77+ list_for_each_entry(entry, &table->entry_list, entry_node) {
78+ if (!memcmp(entry->eth_addr, entry_addr, ETH_ALEN)) {
79+ spin_unlock(&mdb->tbl_lock);
80+ return 0;
81+ }
82+ }
83+ entry = kzalloc(sizeof(struct multicast_table_entry), GFP_ATOMIC);
84+ if (!entry) {
85+ spin_unlock(&mdb->tbl_lock);
86+ return -ENOMEM;
87+ }
88+
89+ memcpy(entry->eth_addr, entry_addr, ETH_ALEN);
90+ list_add(&entry->entry_node, &table->entry_list);
91+ spin_unlock(&mdb->tbl_lock);
92+ return 0;
93+ }
94+ }
95+
96+ table = kzalloc(sizeof(struct multicast_table), GFP_ATOMIC);
97+ if (!table) {
98+ spin_unlock(&mdb->tbl_lock);
99+ return -ENOMEM;
100+ }
101+
102+ INIT_LIST_HEAD(&table->entry_list);
103+ entry = kzalloc(sizeof(struct multicast_table_entry), GFP_ATOMIC);
104+ if (!entry) {
105+ kfree(table);
106+ spin_unlock(&mdb->tbl_lock);
107+ return -ENOMEM;
108+ }
109+
110+ memcpy(entry->eth_addr, entry_addr, ETH_ALEN);
111+ list_add(&entry->entry_node, &table->entry_list);
112+
113+ table->group_addr.u.ip4 = _group_addr;
114+ list_add(&table->mdb_node, &mdb->list_head);
115+
116+ spin_unlock(&mdb->tbl_lock);
117+ return 0;
118+}
119+
120+static int ovs_ip4_multicast_leave_group(__be32 _group_addr,
121+ const u8 *entry_addr,
122+ struct vport *input_vport)
123+{
124+ struct multicast_data_base *mdb;
125+ struct multicast_table *table, *table_tmp;
126+ struct multicast_table_entry *entry, *entry_tmp;
127+
128+ if (ipv4_is_local_multicast(_group_addr))
129+ return 0;
130+
131+ mdb = input_vport->mdb;
132+ spin_lock(&mdb->tbl_lock);
133+ list_for_each_entry_safe(table, table_tmp, &mdb->list_head, mdb_node) {
134+ if (table->group_addr.u.ip4 == _group_addr) {
135+ list_for_each_entry_safe(entry, entry_tmp, &table->entry_list, entry_node) {
136+ if (!memcmp(entry->eth_addr, entry_addr, ETH_ALEN)) {
137+ list_del(&entry->entry_node);
138+ kfree(entry);
139+
140+ if (list_empty(&table->entry_list)) {
141+ list_del(&table->mdb_node);
142+ kfree(table);
143+ }
144+ spin_unlock(&mdb->tbl_lock);
145+ return 0;
146+ }
147+ }
148+ }
149+ }
150+ spin_unlock(&mdb->tbl_lock);
151+ return 0;
152+}
153+
154+static int ovs_multicast_ipv4_rcv(struct sk_buff *skb, struct vport *input_vport)
155+{
156+ struct ethhdr *eth_hdr;
157+ const u8 *dl_src;
158+ __be32 group_addr;
159+ struct iphdr *ip_header;
160+ struct igmphdr *igmp_hdr;
161+ int i;
162+ struct igmpv3_report *igmpv3_hdr;
163+ u16 group_num;
164+ struct igmpv3_grec *grec;
165+ u8 group_type;
166+ u8 aux_data_len;
167+ u16 num_of_source;
168+ int err;
169+
170+ err = ip_mc_check_igmp(skb);
171+ if (err < 0)
172+ return 0;
173+
174+ eth_hdr = skb_eth_hdr(skb);
175+ dl_src = eth_hdr->h_source;
176+ ip_header = (struct iphdr *)(skb->data + 14);
177+ igmp_hdr = (struct igmphdr *)((u8 *)ip_header + ip_header->ihl * 4);
178+
179+ switch (igmp_hdr->type) {
180+ case IGMP_HOST_MEMBERSHIP_REPORT:
181+ case IGMPV2_HOST_MEMBERSHIP_REPORT:
182+ group_addr = igmp_hdr->group;
183+ ovs_ip4_multicast_add_group(group_addr, dl_src, input_vport);
184+ break;
185+ case IGMP_HOST_LEAVE_MESSAGE:
186+ group_addr = igmp_hdr->group;
187+ ovs_ip4_multicast_leave_group(group_addr, dl_src, input_vport);
188+ break;
189+ case IGMPV3_HOST_MEMBERSHIP_REPORT:
190+ igmpv3_hdr = (struct igmpv3_report *)igmp_hdr;
191+ group_num = ntohs(igmpv3_hdr->ngrec);
192+ grec = igmpv3_hdr->grec;
193+ //group_num = ntohs(*(u16 *)(igmp_hdr + 6));
194+ //group = igmp_hdr + 8;
195+ for (i = 0; i < group_num; i++) {
196+ group_type = grec->grec_type;
197+ aux_data_len = grec->grec_auxwords;
198+ num_of_source = ntohs(grec->grec_nsrcs);
199+ group_addr = grec->grec_mca;
200+
201+ if (group_type == IGMPV3_MODE_IS_EXCLUDE ||
202+ group_type == IGMPV3_CHANGE_TO_EXCLUDE ||
203+ group_type == IGMPV3_ALLOW_NEW_SOURCES)
204+ ovs_ip4_multicast_add_group(group_addr, dl_src, input_vport);
205+
206+ if (group_type == IGMPV3_MODE_IS_INCLUDE ||
207+ group_type == IGMPV3_CHANGE_TO_INCLUDE ||
208+ group_type == IGMPV3_BLOCK_OLD_SOURCES)
209+ if (num_of_source == 0)
210+ ovs_ip4_multicast_leave_group(group_addr, dl_src, input_vport);
211+
212+ grec += (8 + (num_of_source * 4) + aux_data_len);
213+ }
214+ break;
215+ default:
216+ pr_warning("%s(): error pkt\n", __func__);
217+ break;
218+ }
219+ return 0;
220+}
221+
222 static int ovs_packet_cmd_execute(struct sk_buff *skb, struct genl_info *info)
223 {
224 struct ovs_header *ovs_header = info->userhdr;
225@@ -604,6 +765,9 @@ static int ovs_packet_cmd_execute(struct sk_buff *skb, struct genl_info *info)
226 OVS_CB(packet)->input_vport = input_vport;
227 sf_acts = rcu_dereference(flow->sf_acts);
228
229+ if (is_igmp(packet))
230+ ovs_multicast_ipv4_rcv(packet, input_vport);
231+
232 local_bh_disable();
233 err = ovs_execute_actions(dp, packet, sf_acts, &flow->key);
234 local_bh_enable();
235@@ -2183,6 +2347,9 @@ static int ovs_vport_cmd_del(struct sk_buff *skb, struct genl_info *info)
236 struct datapath *dp;
237 struct vport *vport;
238 unsigned int new_headroom;
239+ struct multicast_data_base *mdb;
240+ struct multicast_table *table, *table_tmp;
241+ struct multicast_table_entry *entry, *entry_tmp;
242 int err;
243
244 reply = ovs_vport_cmd_alloc_info();
245@@ -2210,6 +2377,22 @@ static int ovs_vport_cmd_del(struct sk_buff *skb, struct genl_info *info)
246 if (netdev_get_fwd_headroom(vport->dev) == dp->max_headroom)
247 update_headroom = true;
248
249+ mdb = vport->mdb;
250+ spin_lock(&mdb->tbl_lock);
251+ list_for_each_entry_safe(table, table_tmp, &mdb->list_head, mdb_node) {
252+ list_for_each_entry_safe(entry, entry_tmp, &table->entry_list, entry_node) {
253+ list_del(&entry->entry_node);
254+ kfree(entry);
255+
256+ if (list_empty(&table->entry_list)) {
257+ list_del(&table->mdb_node);
258+ kfree(table);
259+ }
260+ }
261+ }
262+ spin_unlock(&mdb->tbl_lock);
263+ kfree(mdb);
264+
265 netdev_reset_rx_headroom(vport->dev);
266 ovs_dp_detach_port(vport);
267
268diff --git a/net/openvswitch/datapath.h b/net/openvswitch/datapath.h
269index 81e85dd..520532e 100644
270--- a/net/openvswitch/datapath.h
271+++ b/net/openvswitch/datapath.h
272@@ -215,6 +215,31 @@ static inline struct datapath *get_dp(struct net *net, int dp_ifindex)
273 return dp;
274 }
275
276+#define IGMP_PROTOCOL_OFFSET 23
277+/* support ipv4 for now */
278+static inline bool is_ipv4_multicast(struct sk_buff *skb)
279+{
280+ struct ethhdr *eth_hdr = skb_eth_hdr(skb);
281+
282+ return eth_hdr->h_dest[0] == 0x01 && skb->protocol == htons(ETH_P_IP);
283+}
284+
285+static inline bool is_igmp(struct sk_buff *skb)
286+{
287+ struct ethhdr *eth_hdr;
288+
289+ if (!skb)
290+ return 0;
291+
292+ eth_hdr = skb_eth_hdr(skb);
293+
294+ if (eth_hdr->h_dest[0] == 0x01 &&
295+ skb->protocol == htons(ETH_P_IP))
296+ return (*(skb->data + IGMP_PROTOCOL_OFFSET) == IPPROTO_IGMP);
297+ else
298+ return 0;
299+}
300+
301 extern struct notifier_block ovs_dp_device_notifier;
302 extern struct genl_family dp_vport_genl_family;
303
304diff --git a/net/openvswitch/vport.c b/net/openvswitch/vport.c
305index 19af0ef..77bc923 100644
306--- a/net/openvswitch/vport.c
307+++ b/net/openvswitch/vport.c
308@@ -141,6 +141,14 @@ struct vport *ovs_vport_alloc(int priv_size, const struct vport_ops *ops,
309 return ERR_PTR(-EINVAL);
310 }
311
312+ vport->mdb = kzalloc(sizeof(struct multicast_data_base), GFP_KERNEL);
313+ if (!vport->mdb) {
314+ kfree(vport);
315+ return ERR_PTR(-ENOMEM);
316+ }
317+ INIT_LIST_HEAD(&vport->mdb->list_head);
318+ spin_lock_init(&vport->mdb->tbl_lock);
319+
320 return vport;
321 }
322 EXPORT_SYMBOL_GPL(ovs_vport_alloc);
323diff --git a/net/openvswitch/vport.h b/net/openvswitch/vport.h
324index 1eb7495..eb69d6c 100644
325--- a/net/openvswitch/vport.h
326+++ b/net/openvswitch/vport.h
327@@ -55,6 +55,30 @@ struct vport_portids {
328 u32 ids[];
329 };
330
331+struct ip_addr {
332+ union {
333+ __be32 ip4;
334+ struct in6_addr ip6;
335+ } u;
336+};
337+
338+struct multicast_table_entry {
339+ struct list_head entry_node;
340+ u8 eth_addr[ETH_ALEN];
341+};
342+
343+struct multicast_table {
344+ struct list_head mdb_node;
345+ struct list_head entry_list;
346+ struct ip_addr group_addr;
347+};
348+
349+struct multicast_data_base {
350+ struct list_head list_head;
351+ spinlock_t tbl_lock;
352+};
353+
354+
355 /**
356 * struct vport - one port within a datapath
357 * @dev: Pointer to net_device.
358@@ -79,6 +103,8 @@ struct vport {
359
360 struct list_head detach_list;
361 struct rcu_head rcu;
362+
363+ struct multicast_data_base *mdb;
364 };
365
366 /**