]> git.kernelconcepts.de Git - karo-tx-linux.git/blob - net/bridge/br_mdb.c
bridge: export multicast database via netlink
[karo-tx-linux.git] / net / bridge / br_mdb.c
1 #include <linux/err.h>
2 #include <linux/igmp.h>
3 #include <linux/kernel.h>
4 #include <linux/netdevice.h>
5 #include <linux/rculist.h>
6 #include <linux/skbuff.h>
7 #include <net/ip.h>
8 #include <net/netlink.h>
9 #if IS_ENABLED(CONFIG_IPV6)
10 #include <net/ipv6.h>
11 #endif
12
13 #include "br_private.h"
14
15 static int br_rports_fill_info(struct sk_buff *skb, struct netlink_callback *cb,
16                                struct net_device *dev)
17 {
18         struct net_bridge *br = netdev_priv(dev);
19         struct net_bridge_port *p;
20         struct hlist_node *n;
21         struct nlattr *nest;
22
23         if (!br->multicast_router || hlist_empty(&br->router_list))
24                 return 0;
25
26         nest = nla_nest_start(skb, MDBA_ROUTER);
27         if (nest == NULL)
28                 return -EMSGSIZE;
29
30         hlist_for_each_entry_rcu(p, n, &br->router_list, rlist) {
31                 if (p && nla_put_u32(skb, MDBA_ROUTER_PORT, p->dev->ifindex))
32                         goto fail;
33         }
34
35         nla_nest_end(skb, nest);
36         return 0;
37 fail:
38         nla_nest_cancel(skb, nest);
39         return -EMSGSIZE;
40 }
41
42 static int br_mdb_fill_info(struct sk_buff *skb, struct netlink_callback *cb,
43                             struct net_device *dev)
44 {
45         struct net_bridge *br = netdev_priv(dev);
46         struct net_bridge_mdb_htable *mdb;
47         struct nlattr *nest, *nest2;
48         int i, err = 0;
49         int idx = 0, s_idx = cb->args[1];
50
51         if (br->multicast_disabled)
52                 return 0;
53
54         mdb = rcu_dereference(br->mdb);
55         if (!mdb)
56                 return 0;
57
58         nest = nla_nest_start(skb, MDBA_MDB);
59         if (nest == NULL)
60                 return -EMSGSIZE;
61
62         for (i = 0; i < mdb->max; i++) {
63                 struct hlist_node *h;
64                 struct net_bridge_mdb_entry *mp;
65                 struct net_bridge_port_group *p, **pp;
66                 struct net_bridge_port *port;
67
68                 hlist_for_each_entry_rcu(mp, h, &mdb->mhash[i], hlist[mdb->ver]) {
69                         if (idx < s_idx)
70                                 goto skip;
71
72                         nest2 = nla_nest_start(skb, MDBA_MDB_ENTRY);
73                         if (nest2 == NULL) {
74                                 err = -EMSGSIZE;
75                                 goto out;
76                         }
77
78                         for (pp = &mp->ports;
79                              (p = rcu_dereference(*pp)) != NULL;
80                               pp = &p->next) {
81                                 port = p->port;
82                                 if (port) {
83                                         struct br_mdb_entry e;
84                                         e.ifindex = port->dev->ifindex;
85                                         e.addr.u.ip4 = p->addr.u.ip4;
86 #if IS_ENABLED(CONFIG_IPV6)
87                                         e.addr.u.ip6 = p->addr.u.ip6;
88 #endif
89                                         e.addr.proto = p->addr.proto;
90                                         if (nla_put(skb, MDBA_MDB_ENTRY_INFO, sizeof(e), &e)) {
91                                                 nla_nest_cancel(skb, nest2);
92                                                 err = -EMSGSIZE;
93                                                 goto out;
94                                         }
95                                 }
96                         }
97                         nla_nest_end(skb, nest2);
98                 skip:
99                         idx++;
100                 }
101         }
102
103 out:
104         cb->args[1] = idx;
105         nla_nest_end(skb, nest);
106         return err;
107 }
108
109 static int br_mdb_dump(struct sk_buff *skb, struct netlink_callback *cb)
110 {
111         struct net_device *dev;
112         struct net *net = sock_net(skb->sk);
113         struct nlmsghdr *nlh = NULL;
114         int idx = 0, s_idx;
115
116         s_idx = cb->args[0];
117
118         rcu_read_lock();
119
120         /* TODO: in case of rehashing, we need to check
121          * consistency for dumping.
122          */
123         cb->seq = net->dev_base_seq;
124
125         for_each_netdev_rcu(net, dev) {
126                 if (dev->priv_flags & IFF_EBRIDGE) {
127                         struct br_port_msg *bpm;
128
129                         if (idx < s_idx)
130                                 goto skip;
131
132                         nlh = nlmsg_put(skb, NETLINK_CB(cb->skb).portid,
133                                         cb->nlh->nlmsg_seq, RTM_GETMDB,
134                                         sizeof(*bpm), NLM_F_MULTI);
135                         if (nlh == NULL)
136                                 break;
137
138                         bpm = nlmsg_data(nlh);
139                         bpm->ifindex = dev->ifindex;
140                         if (br_mdb_fill_info(skb, cb, dev) < 0)
141                                 goto out;
142                         if (br_rports_fill_info(skb, cb, dev) < 0)
143                                 goto out;
144
145                         cb->args[1] = 0;
146                         nlmsg_end(skb, nlh);
147                 skip:
148                         idx++;
149                 }
150         }
151
152 out:
153         if (nlh)
154                 nlmsg_end(skb, nlh);
155         rcu_read_unlock();
156         cb->args[0] = idx;
157         return skb->len;
158 }
159
160 void br_mdb_init(void)
161 {
162         rtnl_register(PF_BRIDGE, RTM_GETMDB, NULL, br_mdb_dump, NULL);
163 }