diff options
author | Guenter Roeck <linux@roeck-us.net> | 2015-03-26 21:36:37 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2015-03-29 16:23:54 -0400 |
commit | 339d82626d225e9b876665e4e89b7eb123e96b3d (patch) | |
tree | 03ef0d816547cd8423c6c2e89c651926fb228bb8 | |
parent | 3f244abb53665bf1ae0a762bb452d33b6648b7df (diff) |
net: dsa: Add basic framework to support ndo_fdb functions
Provide callbacks for ndo_fdb_add, ndo_fdb_del, and ndo_fdb_dump.
Reviewed-by: Andrew Lunn <andrew@lunn.ch>
Tested-by: Andrew Lunn <andrew@lunn.ch>
Signed-off-by: Guenter Roeck <linux@roeck-us.net>
Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r-- | include/net/dsa.h | 6 | ||||
-rw-r--r-- | net/dsa/slave.c | 102 |
2 files changed, 108 insertions, 0 deletions
diff --git a/include/net/dsa.h b/include/net/dsa.h index 47917e5e1e12..fbca63ba8f73 100644 --- a/include/net/dsa.h +++ b/include/net/dsa.h | |||
@@ -296,6 +296,12 @@ struct dsa_switch_driver { | |||
296 | u32 br_port_mask); | 296 | u32 br_port_mask); |
297 | int (*port_stp_update)(struct dsa_switch *ds, int port, | 297 | int (*port_stp_update)(struct dsa_switch *ds, int port, |
298 | u8 state); | 298 | u8 state); |
299 | int (*fdb_add)(struct dsa_switch *ds, int port, | ||
300 | const unsigned char *addr, u16 vid); | ||
301 | int (*fdb_del)(struct dsa_switch *ds, int port, | ||
302 | const unsigned char *addr, u16 vid); | ||
303 | int (*fdb_getnext)(struct dsa_switch *ds, int port, | ||
304 | unsigned char *addr, bool *is_static); | ||
299 | }; | 305 | }; |
300 | 306 | ||
301 | void register_switch_driver(struct dsa_switch_driver *type); | 307 | void register_switch_driver(struct dsa_switch_driver *type); |
diff --git a/net/dsa/slave.c b/net/dsa/slave.c index 39555f3f263b..3597724ec3d8 100644 --- a/net/dsa/slave.c +++ b/net/dsa/slave.c | |||
@@ -201,6 +201,105 @@ out: | |||
201 | return 0; | 201 | return 0; |
202 | } | 202 | } |
203 | 203 | ||
204 | static int dsa_slave_fdb_add(struct ndmsg *ndm, struct nlattr *tb[], | ||
205 | struct net_device *dev, | ||
206 | const unsigned char *addr, u16 vid, u16 nlm_flags) | ||
207 | { | ||
208 | struct dsa_slave_priv *p = netdev_priv(dev); | ||
209 | struct dsa_switch *ds = p->parent; | ||
210 | int ret = -EOPNOTSUPP; | ||
211 | |||
212 | if (ds->drv->fdb_add) | ||
213 | ret = ds->drv->fdb_add(ds, p->port, addr, vid); | ||
214 | |||
215 | return ret; | ||
216 | } | ||
217 | |||
218 | static int dsa_slave_fdb_del(struct ndmsg *ndm, struct nlattr *tb[], | ||
219 | struct net_device *dev, | ||
220 | const unsigned char *addr, u16 vid) | ||
221 | { | ||
222 | struct dsa_slave_priv *p = netdev_priv(dev); | ||
223 | struct dsa_switch *ds = p->parent; | ||
224 | int ret = -EOPNOTSUPP; | ||
225 | |||
226 | if (ds->drv->fdb_del) | ||
227 | ret = ds->drv->fdb_del(ds, p->port, addr, vid); | ||
228 | |||
229 | return ret; | ||
230 | } | ||
231 | |||
232 | static int dsa_slave_fill_info(struct net_device *dev, struct sk_buff *skb, | ||
233 | const unsigned char *addr, u16 vid, | ||
234 | bool is_static, | ||
235 | u32 portid, u32 seq, int type, | ||
236 | unsigned int flags) | ||
237 | { | ||
238 | struct nlmsghdr *nlh; | ||
239 | struct ndmsg *ndm; | ||
240 | |||
241 | nlh = nlmsg_put(skb, portid, seq, type, sizeof(*ndm), flags); | ||
242 | if (!nlh) | ||
243 | return -EMSGSIZE; | ||
244 | |||
245 | ndm = nlmsg_data(nlh); | ||
246 | ndm->ndm_family = AF_BRIDGE; | ||
247 | ndm->ndm_pad1 = 0; | ||
248 | ndm->ndm_pad2 = 0; | ||
249 | ndm->ndm_flags = NTF_EXT_LEARNED; | ||
250 | ndm->ndm_type = 0; | ||
251 | ndm->ndm_ifindex = dev->ifindex; | ||
252 | ndm->ndm_state = is_static ? NUD_NOARP : NUD_REACHABLE; | ||
253 | |||
254 | if (nla_put(skb, NDA_LLADDR, ETH_ALEN, addr)) | ||
255 | goto nla_put_failure; | ||
256 | |||
257 | if (vid && nla_put_u16(skb, NDA_VLAN, vid)) | ||
258 | goto nla_put_failure; | ||
259 | |||
260 | nlmsg_end(skb, nlh); | ||
261 | return 0; | ||
262 | |||
263 | nla_put_failure: | ||
264 | nlmsg_cancel(skb, nlh); | ||
265 | return -EMSGSIZE; | ||
266 | } | ||
267 | |||
268 | /* Dump information about entries, in response to GETNEIGH */ | ||
269 | static int dsa_slave_fdb_dump(struct sk_buff *skb, struct netlink_callback *cb, | ||
270 | struct net_device *dev, | ||
271 | struct net_device *filter_dev, int idx) | ||
272 | { | ||
273 | struct dsa_slave_priv *p = netdev_priv(dev); | ||
274 | struct dsa_switch *ds = p->parent; | ||
275 | unsigned char addr[ETH_ALEN] = { 0 }; | ||
276 | int ret; | ||
277 | |||
278 | if (!ds->drv->fdb_getnext) | ||
279 | return -EOPNOTSUPP; | ||
280 | |||
281 | for (; ; idx++) { | ||
282 | bool is_static; | ||
283 | |||
284 | ret = ds->drv->fdb_getnext(ds, p->port, addr, &is_static); | ||
285 | if (ret < 0) | ||
286 | break; | ||
287 | |||
288 | if (idx < cb->args[0]) | ||
289 | continue; | ||
290 | |||
291 | ret = dsa_slave_fill_info(dev, skb, addr, 0, | ||
292 | is_static, | ||
293 | NETLINK_CB(cb->skb).portid, | ||
294 | cb->nlh->nlmsg_seq, | ||
295 | RTM_NEWNEIGH, NLM_F_MULTI); | ||
296 | if (ret < 0) | ||
297 | break; | ||
298 | } | ||
299 | |||
300 | return idx; | ||
301 | } | ||
302 | |||
204 | static int dsa_slave_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) | 303 | static int dsa_slave_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) |
205 | { | 304 | { |
206 | struct dsa_slave_priv *p = netdev_priv(dev); | 305 | struct dsa_slave_priv *p = netdev_priv(dev); |
@@ -572,6 +671,9 @@ static const struct net_device_ops dsa_slave_netdev_ops = { | |||
572 | .ndo_change_rx_flags = dsa_slave_change_rx_flags, | 671 | .ndo_change_rx_flags = dsa_slave_change_rx_flags, |
573 | .ndo_set_rx_mode = dsa_slave_set_rx_mode, | 672 | .ndo_set_rx_mode = dsa_slave_set_rx_mode, |
574 | .ndo_set_mac_address = dsa_slave_set_mac_address, | 673 | .ndo_set_mac_address = dsa_slave_set_mac_address, |
674 | .ndo_fdb_add = dsa_slave_fdb_add, | ||
675 | .ndo_fdb_del = dsa_slave_fdb_del, | ||
676 | .ndo_fdb_dump = dsa_slave_fdb_dump, | ||
575 | .ndo_do_ioctl = dsa_slave_ioctl, | 677 | .ndo_do_ioctl = dsa_slave_ioctl, |
576 | }; | 678 | }; |
577 | 679 | ||