xref: /linux-6.15/net/switchdev/switchdev.c (revision b5d6fbde)
1007f790cSJiri Pirko /*
2007f790cSJiri Pirko  * net/switchdev/switchdev.c - Switch device API
3007f790cSJiri Pirko  * Copyright (c) 2014 Jiri Pirko <[email protected]>
4007f790cSJiri Pirko  *
5007f790cSJiri Pirko  * This program is free software; you can redistribute it and/or modify
6007f790cSJiri Pirko  * it under the terms of the GNU General Public License as published by
7007f790cSJiri Pirko  * the Free Software Foundation; either version 2 of the License, or
8007f790cSJiri Pirko  * (at your option) any later version.
9007f790cSJiri Pirko  */
10007f790cSJiri Pirko 
11007f790cSJiri Pirko #include <linux/kernel.h>
12007f790cSJiri Pirko #include <linux/types.h>
13007f790cSJiri Pirko #include <linux/init.h>
1403bf0c28SJiri Pirko #include <linux/mutex.h>
1503bf0c28SJiri Pirko #include <linux/notifier.h>
16007f790cSJiri Pirko #include <linux/netdevice.h>
175e8d9049SScott Feldman #include <net/ip_fib.h>
18007f790cSJiri Pirko #include <net/switchdev.h>
19007f790cSJiri Pirko 
20007f790cSJiri Pirko /**
21007f790cSJiri Pirko  *	netdev_switch_parent_id_get - Get ID of a switch
22007f790cSJiri Pirko  *	@dev: port device
23007f790cSJiri Pirko  *	@psid: switch ID
24007f790cSJiri Pirko  *
25007f790cSJiri Pirko  *	Get ID of a switch this port is part of.
26007f790cSJiri Pirko  */
27007f790cSJiri Pirko int netdev_switch_parent_id_get(struct net_device *dev,
28007f790cSJiri Pirko 				struct netdev_phys_item_id *psid)
29007f790cSJiri Pirko {
30007f790cSJiri Pirko 	const struct net_device_ops *ops = dev->netdev_ops;
31007f790cSJiri Pirko 
32007f790cSJiri Pirko 	if (!ops->ndo_switch_parent_id_get)
33007f790cSJiri Pirko 		return -EOPNOTSUPP;
34007f790cSJiri Pirko 	return ops->ndo_switch_parent_id_get(dev, psid);
35007f790cSJiri Pirko }
36007f790cSJiri Pirko EXPORT_SYMBOL(netdev_switch_parent_id_get);
3738dcf357SScott Feldman 
3838dcf357SScott Feldman /**
3938dcf357SScott Feldman  *	netdev_switch_port_stp_update - Notify switch device port of STP
4038dcf357SScott Feldman  *					state change
4138dcf357SScott Feldman  *	@dev: port device
4238dcf357SScott Feldman  *	@state: port STP state
4338dcf357SScott Feldman  *
4438dcf357SScott Feldman  *	Notify switch device port of bridge port STP state change.
4538dcf357SScott Feldman  */
4638dcf357SScott Feldman int netdev_switch_port_stp_update(struct net_device *dev, u8 state)
4738dcf357SScott Feldman {
4838dcf357SScott Feldman 	const struct net_device_ops *ops = dev->netdev_ops;
4938dcf357SScott Feldman 
5038dcf357SScott Feldman 	if (!ops->ndo_switch_port_stp_update)
5138dcf357SScott Feldman 		return -EOPNOTSUPP;
5238dcf357SScott Feldman 	WARN_ON(!ops->ndo_switch_parent_id_get);
5338dcf357SScott Feldman 	return ops->ndo_switch_port_stp_update(dev, state);
5438dcf357SScott Feldman }
5538dcf357SScott Feldman EXPORT_SYMBOL(netdev_switch_port_stp_update);
5603bf0c28SJiri Pirko 
5703bf0c28SJiri Pirko static DEFINE_MUTEX(netdev_switch_mutex);
5803bf0c28SJiri Pirko static RAW_NOTIFIER_HEAD(netdev_switch_notif_chain);
5903bf0c28SJiri Pirko 
6003bf0c28SJiri Pirko /**
6103bf0c28SJiri Pirko  *	register_netdev_switch_notifier - Register nofifier
6203bf0c28SJiri Pirko  *	@nb: notifier_block
6303bf0c28SJiri Pirko  *
6403bf0c28SJiri Pirko  *	Register switch device notifier. This should be used by code
6503bf0c28SJiri Pirko  *	which needs to monitor events happening in particular device.
6603bf0c28SJiri Pirko  *	Return values are same as for atomic_notifier_chain_register().
6703bf0c28SJiri Pirko  */
6803bf0c28SJiri Pirko int register_netdev_switch_notifier(struct notifier_block *nb)
6903bf0c28SJiri Pirko {
7003bf0c28SJiri Pirko 	int err;
7103bf0c28SJiri Pirko 
7203bf0c28SJiri Pirko 	mutex_lock(&netdev_switch_mutex);
7303bf0c28SJiri Pirko 	err = raw_notifier_chain_register(&netdev_switch_notif_chain, nb);
7403bf0c28SJiri Pirko 	mutex_unlock(&netdev_switch_mutex);
7503bf0c28SJiri Pirko 	return err;
7603bf0c28SJiri Pirko }
7703bf0c28SJiri Pirko EXPORT_SYMBOL(register_netdev_switch_notifier);
7803bf0c28SJiri Pirko 
7903bf0c28SJiri Pirko /**
8003bf0c28SJiri Pirko  *	unregister_netdev_switch_notifier - Unregister nofifier
8103bf0c28SJiri Pirko  *	@nb: notifier_block
8203bf0c28SJiri Pirko  *
8303bf0c28SJiri Pirko  *	Unregister switch device notifier.
8403bf0c28SJiri Pirko  *	Return values are same as for atomic_notifier_chain_unregister().
8503bf0c28SJiri Pirko  */
8603bf0c28SJiri Pirko int unregister_netdev_switch_notifier(struct notifier_block *nb)
8703bf0c28SJiri Pirko {
8803bf0c28SJiri Pirko 	int err;
8903bf0c28SJiri Pirko 
9003bf0c28SJiri Pirko 	mutex_lock(&netdev_switch_mutex);
9103bf0c28SJiri Pirko 	err = raw_notifier_chain_unregister(&netdev_switch_notif_chain, nb);
9203bf0c28SJiri Pirko 	mutex_unlock(&netdev_switch_mutex);
9303bf0c28SJiri Pirko 	return err;
9403bf0c28SJiri Pirko }
9503bf0c28SJiri Pirko EXPORT_SYMBOL(unregister_netdev_switch_notifier);
9603bf0c28SJiri Pirko 
9703bf0c28SJiri Pirko /**
9803bf0c28SJiri Pirko  *	call_netdev_switch_notifiers - Call nofifiers
9903bf0c28SJiri Pirko  *	@val: value passed unmodified to notifier function
10003bf0c28SJiri Pirko  *	@dev: port device
10103bf0c28SJiri Pirko  *	@info: notifier information data
10203bf0c28SJiri Pirko  *
10303bf0c28SJiri Pirko  *	Call all network notifier blocks. This should be called by driver
10403bf0c28SJiri Pirko  *	when it needs to propagate hardware event.
10503bf0c28SJiri Pirko  *	Return values are same as for atomic_notifier_call_chain().
10603bf0c28SJiri Pirko  */
10703bf0c28SJiri Pirko int call_netdev_switch_notifiers(unsigned long val, struct net_device *dev,
10803bf0c28SJiri Pirko 				 struct netdev_switch_notifier_info *info)
10903bf0c28SJiri Pirko {
11003bf0c28SJiri Pirko 	int err;
11103bf0c28SJiri Pirko 
11203bf0c28SJiri Pirko 	info->dev = dev;
11303bf0c28SJiri Pirko 	mutex_lock(&netdev_switch_mutex);
11403bf0c28SJiri Pirko 	err = raw_notifier_call_chain(&netdev_switch_notif_chain, val, info);
11503bf0c28SJiri Pirko 	mutex_unlock(&netdev_switch_mutex);
11603bf0c28SJiri Pirko 	return err;
11703bf0c28SJiri Pirko }
11803bf0c28SJiri Pirko EXPORT_SYMBOL(call_netdev_switch_notifiers);
1198a44dbb2SRoopa Prabhu 
1208a44dbb2SRoopa Prabhu /**
1218a44dbb2SRoopa Prabhu  *	netdev_switch_port_bridge_setlink - Notify switch device port of bridge
1228a44dbb2SRoopa Prabhu  *	port attributes
1238a44dbb2SRoopa Prabhu  *
1248a44dbb2SRoopa Prabhu  *	@dev: port device
1258a44dbb2SRoopa Prabhu  *	@nlh: netlink msg with bridge port attributes
1268a44dbb2SRoopa Prabhu  *	@flags: bridge setlink flags
1278a44dbb2SRoopa Prabhu  *
1288a44dbb2SRoopa Prabhu  *	Notify switch device port of bridge port attributes
1298a44dbb2SRoopa Prabhu  */
1308a44dbb2SRoopa Prabhu int netdev_switch_port_bridge_setlink(struct net_device *dev,
1318a44dbb2SRoopa Prabhu 				      struct nlmsghdr *nlh, u16 flags)
1328a44dbb2SRoopa Prabhu {
1338a44dbb2SRoopa Prabhu 	const struct net_device_ops *ops = dev->netdev_ops;
1348a44dbb2SRoopa Prabhu 
1358a44dbb2SRoopa Prabhu 	if (!(dev->features & NETIF_F_HW_SWITCH_OFFLOAD))
1368a44dbb2SRoopa Prabhu 		return 0;
1378a44dbb2SRoopa Prabhu 
1388a44dbb2SRoopa Prabhu 	if (!ops->ndo_bridge_setlink)
1398a44dbb2SRoopa Prabhu 		return -EOPNOTSUPP;
1408a44dbb2SRoopa Prabhu 
1418a44dbb2SRoopa Prabhu 	return ops->ndo_bridge_setlink(dev, nlh, flags);
1428a44dbb2SRoopa Prabhu }
1438a44dbb2SRoopa Prabhu EXPORT_SYMBOL(netdev_switch_port_bridge_setlink);
1448a44dbb2SRoopa Prabhu 
1458a44dbb2SRoopa Prabhu /**
1468a44dbb2SRoopa Prabhu  *	netdev_switch_port_bridge_dellink - Notify switch device port of bridge
1478a44dbb2SRoopa Prabhu  *	port attribute delete
1488a44dbb2SRoopa Prabhu  *
1498a44dbb2SRoopa Prabhu  *	@dev: port device
1508a44dbb2SRoopa Prabhu  *	@nlh: netlink msg with bridge port attributes
1518a44dbb2SRoopa Prabhu  *	@flags: bridge setlink flags
1528a44dbb2SRoopa Prabhu  *
1538a44dbb2SRoopa Prabhu  *	Notify switch device port of bridge port attribute delete
1548a44dbb2SRoopa Prabhu  */
1558a44dbb2SRoopa Prabhu int netdev_switch_port_bridge_dellink(struct net_device *dev,
1568a44dbb2SRoopa Prabhu 				      struct nlmsghdr *nlh, u16 flags)
1578a44dbb2SRoopa Prabhu {
1588a44dbb2SRoopa Prabhu 	const struct net_device_ops *ops = dev->netdev_ops;
1598a44dbb2SRoopa Prabhu 
1608a44dbb2SRoopa Prabhu 	if (!(dev->features & NETIF_F_HW_SWITCH_OFFLOAD))
1618a44dbb2SRoopa Prabhu 		return 0;
1628a44dbb2SRoopa Prabhu 
1638a44dbb2SRoopa Prabhu 	if (!ops->ndo_bridge_dellink)
1648a44dbb2SRoopa Prabhu 		return -EOPNOTSUPP;
1658a44dbb2SRoopa Prabhu 
1668a44dbb2SRoopa Prabhu 	return ops->ndo_bridge_dellink(dev, nlh, flags);
1678a44dbb2SRoopa Prabhu }
1688a44dbb2SRoopa Prabhu EXPORT_SYMBOL(netdev_switch_port_bridge_dellink);
1698a44dbb2SRoopa Prabhu 
1708a44dbb2SRoopa Prabhu /**
1718a44dbb2SRoopa Prabhu  *	ndo_dflt_netdev_switch_port_bridge_setlink - default ndo bridge setlink
1728a44dbb2SRoopa Prabhu  *						     op for master devices
1738a44dbb2SRoopa Prabhu  *
1748a44dbb2SRoopa Prabhu  *	@dev: port device
1758a44dbb2SRoopa Prabhu  *	@nlh: netlink msg with bridge port attributes
1768a44dbb2SRoopa Prabhu  *	@flags: bridge setlink flags
1778a44dbb2SRoopa Prabhu  *
1788a44dbb2SRoopa Prabhu  *	Notify master device slaves of bridge port attributes
1798a44dbb2SRoopa Prabhu  */
1808a44dbb2SRoopa Prabhu int ndo_dflt_netdev_switch_port_bridge_setlink(struct net_device *dev,
1818a44dbb2SRoopa Prabhu 					       struct nlmsghdr *nlh, u16 flags)
1828a44dbb2SRoopa Prabhu {
1838a44dbb2SRoopa Prabhu 	struct net_device *lower_dev;
1848a44dbb2SRoopa Prabhu 	struct list_head *iter;
1858a44dbb2SRoopa Prabhu 	int ret = 0, err = 0;
1868a44dbb2SRoopa Prabhu 
1878a44dbb2SRoopa Prabhu 	if (!(dev->features & NETIF_F_HW_SWITCH_OFFLOAD))
1888a44dbb2SRoopa Prabhu 		return ret;
1898a44dbb2SRoopa Prabhu 
1908a44dbb2SRoopa Prabhu 	netdev_for_each_lower_dev(dev, lower_dev, iter) {
1918a44dbb2SRoopa Prabhu 		err = netdev_switch_port_bridge_setlink(lower_dev, nlh, flags);
1928a44dbb2SRoopa Prabhu 		if (err && err != -EOPNOTSUPP)
1938a44dbb2SRoopa Prabhu 			ret = err;
1948a44dbb2SRoopa Prabhu 	}
1958a44dbb2SRoopa Prabhu 
1968a44dbb2SRoopa Prabhu 	return ret;
1978a44dbb2SRoopa Prabhu }
1988a44dbb2SRoopa Prabhu EXPORT_SYMBOL(ndo_dflt_netdev_switch_port_bridge_setlink);
1998a44dbb2SRoopa Prabhu 
2008a44dbb2SRoopa Prabhu /**
2018a44dbb2SRoopa Prabhu  *	ndo_dflt_netdev_switch_port_bridge_dellink - default ndo bridge dellink
2028a44dbb2SRoopa Prabhu  *						     op for master devices
2038a44dbb2SRoopa Prabhu  *
2048a44dbb2SRoopa Prabhu  *	@dev: port device
2058a44dbb2SRoopa Prabhu  *	@nlh: netlink msg with bridge port attributes
2068a44dbb2SRoopa Prabhu  *	@flags: bridge dellink flags
2078a44dbb2SRoopa Prabhu  *
2088a44dbb2SRoopa Prabhu  *	Notify master device slaves of bridge port attribute deletes
2098a44dbb2SRoopa Prabhu  */
2108a44dbb2SRoopa Prabhu int ndo_dflt_netdev_switch_port_bridge_dellink(struct net_device *dev,
2118a44dbb2SRoopa Prabhu 					       struct nlmsghdr *nlh, u16 flags)
2128a44dbb2SRoopa Prabhu {
2138a44dbb2SRoopa Prabhu 	struct net_device *lower_dev;
2148a44dbb2SRoopa Prabhu 	struct list_head *iter;
2158a44dbb2SRoopa Prabhu 	int ret = 0, err = 0;
2168a44dbb2SRoopa Prabhu 
2178a44dbb2SRoopa Prabhu 	if (!(dev->features & NETIF_F_HW_SWITCH_OFFLOAD))
2188a44dbb2SRoopa Prabhu 		return ret;
2198a44dbb2SRoopa Prabhu 
2208a44dbb2SRoopa Prabhu 	netdev_for_each_lower_dev(dev, lower_dev, iter) {
2218a44dbb2SRoopa Prabhu 		err = netdev_switch_port_bridge_dellink(lower_dev, nlh, flags);
2228a44dbb2SRoopa Prabhu 		if (err && err != -EOPNOTSUPP)
2238a44dbb2SRoopa Prabhu 			ret = err;
2248a44dbb2SRoopa Prabhu 	}
2258a44dbb2SRoopa Prabhu 
2268a44dbb2SRoopa Prabhu 	return ret;
2278a44dbb2SRoopa Prabhu }
2288a44dbb2SRoopa Prabhu EXPORT_SYMBOL(ndo_dflt_netdev_switch_port_bridge_dellink);
2295e8d9049SScott Feldman 
230*b5d6fbdeSScott Feldman static struct net_device *netdev_switch_get_lowest_dev(struct net_device *dev)
231*b5d6fbdeSScott Feldman {
232*b5d6fbdeSScott Feldman 	const struct net_device_ops *ops = dev->netdev_ops;
233*b5d6fbdeSScott Feldman 	struct net_device *lower_dev;
234*b5d6fbdeSScott Feldman 	struct net_device *port_dev;
235*b5d6fbdeSScott Feldman 	struct list_head *iter;
236*b5d6fbdeSScott Feldman 
237*b5d6fbdeSScott Feldman 	/* Recusively search down until we find a sw port dev.
238*b5d6fbdeSScott Feldman 	 * (A sw port dev supports ndo_switch_parent_id_get).
239*b5d6fbdeSScott Feldman 	 */
240*b5d6fbdeSScott Feldman 
241*b5d6fbdeSScott Feldman 	if (dev->features & NETIF_F_HW_SWITCH_OFFLOAD &&
242*b5d6fbdeSScott Feldman 	    ops->ndo_switch_parent_id_get)
243*b5d6fbdeSScott Feldman 		return dev;
244*b5d6fbdeSScott Feldman 
245*b5d6fbdeSScott Feldman 	netdev_for_each_lower_dev(dev, lower_dev, iter) {
246*b5d6fbdeSScott Feldman 		port_dev = netdev_switch_get_lowest_dev(lower_dev);
247*b5d6fbdeSScott Feldman 		if (port_dev)
248*b5d6fbdeSScott Feldman 			return port_dev;
249*b5d6fbdeSScott Feldman 	}
250*b5d6fbdeSScott Feldman 
251*b5d6fbdeSScott Feldman 	return NULL;
252*b5d6fbdeSScott Feldman }
253*b5d6fbdeSScott Feldman 
254*b5d6fbdeSScott Feldman static struct net_device *netdev_switch_get_dev_by_nhs(struct fib_info *fi)
255*b5d6fbdeSScott Feldman {
256*b5d6fbdeSScott Feldman 	struct netdev_phys_item_id psid;
257*b5d6fbdeSScott Feldman 	struct netdev_phys_item_id prev_psid;
258*b5d6fbdeSScott Feldman 	struct net_device *dev = NULL;
259*b5d6fbdeSScott Feldman 	int nhsel;
260*b5d6fbdeSScott Feldman 
261*b5d6fbdeSScott Feldman 	/* For this route, all nexthop devs must be on the same switch. */
262*b5d6fbdeSScott Feldman 
263*b5d6fbdeSScott Feldman 	for (nhsel = 0; nhsel < fi->fib_nhs; nhsel++) {
264*b5d6fbdeSScott Feldman 		const struct fib_nh *nh = &fi->fib_nh[nhsel];
265*b5d6fbdeSScott Feldman 
266*b5d6fbdeSScott Feldman 		if (!nh->nh_dev)
267*b5d6fbdeSScott Feldman 			return NULL;
268*b5d6fbdeSScott Feldman 
269*b5d6fbdeSScott Feldman 		dev = netdev_switch_get_lowest_dev(nh->nh_dev);
270*b5d6fbdeSScott Feldman 		if (!dev)
271*b5d6fbdeSScott Feldman 			return NULL;
272*b5d6fbdeSScott Feldman 
273*b5d6fbdeSScott Feldman 		if (netdev_switch_parent_id_get(dev, &psid))
274*b5d6fbdeSScott Feldman 			return NULL;
275*b5d6fbdeSScott Feldman 
276*b5d6fbdeSScott Feldman 		if (nhsel > 0) {
277*b5d6fbdeSScott Feldman 			if (prev_psid.id_len != psid.id_len)
278*b5d6fbdeSScott Feldman 				return NULL;
279*b5d6fbdeSScott Feldman 			if (memcmp(prev_psid.id, psid.id, psid.id_len))
280*b5d6fbdeSScott Feldman 				return NULL;
281*b5d6fbdeSScott Feldman 		}
282*b5d6fbdeSScott Feldman 
283*b5d6fbdeSScott Feldman 		prev_psid = psid;
284*b5d6fbdeSScott Feldman 	}
285*b5d6fbdeSScott Feldman 
286*b5d6fbdeSScott Feldman 	return dev;
287*b5d6fbdeSScott Feldman }
288*b5d6fbdeSScott Feldman 
2895e8d9049SScott Feldman /**
2905e8d9049SScott Feldman  *	netdev_switch_fib_ipv4_add - Add IPv4 route entry to switch
2915e8d9049SScott Feldman  *
2925e8d9049SScott Feldman  *	@dst: route's IPv4 destination address
2935e8d9049SScott Feldman  *	@dst_len: destination address length (prefix length)
2945e8d9049SScott Feldman  *	@fi: route FIB info structure
2955e8d9049SScott Feldman  *	@tos: route TOS
2965e8d9049SScott Feldman  *	@type: route type
2975e8d9049SScott Feldman  *	@tb_id: route table ID
2985e8d9049SScott Feldman  *
2995e8d9049SScott Feldman  *	Add IPv4 route entry to switch device.
3005e8d9049SScott Feldman  */
3015e8d9049SScott Feldman int netdev_switch_fib_ipv4_add(u32 dst, int dst_len, struct fib_info *fi,
3025e8d9049SScott Feldman 			       u8 tos, u8 type, u32 tb_id)
3035e8d9049SScott Feldman {
304*b5d6fbdeSScott Feldman 	struct net_device *dev;
305*b5d6fbdeSScott Feldman 	const struct net_device_ops *ops;
306*b5d6fbdeSScott Feldman 	int err = 0;
307*b5d6fbdeSScott Feldman 
308104616e7SScott Feldman 	/* Don't offload route if using custom ip rules */
309104616e7SScott Feldman 	if (fi->fib_net->ipv4.fib_has_custom_rules)
310104616e7SScott Feldman 		return 0;
311104616e7SScott Feldman 
312*b5d6fbdeSScott Feldman 	dev = netdev_switch_get_dev_by_nhs(fi);
313*b5d6fbdeSScott Feldman 	if (!dev)
3145e8d9049SScott Feldman 		return 0;
315*b5d6fbdeSScott Feldman 	ops = dev->netdev_ops;
316*b5d6fbdeSScott Feldman 
317*b5d6fbdeSScott Feldman 	if (ops->ndo_switch_fib_ipv4_add) {
318*b5d6fbdeSScott Feldman 		err = ops->ndo_switch_fib_ipv4_add(dev, htonl(dst), dst_len,
319*b5d6fbdeSScott Feldman 						   fi, tos, type, tb_id);
320*b5d6fbdeSScott Feldman 		if (!err)
321*b5d6fbdeSScott Feldman 			fi->fib_flags |= RTNH_F_EXTERNAL;
322*b5d6fbdeSScott Feldman 	}
323*b5d6fbdeSScott Feldman 
324*b5d6fbdeSScott Feldman 	return err;
3255e8d9049SScott Feldman }
3265e8d9049SScott Feldman EXPORT_SYMBOL(netdev_switch_fib_ipv4_add);
3275e8d9049SScott Feldman 
3285e8d9049SScott Feldman /**
3295e8d9049SScott Feldman  *	netdev_switch_fib_ipv4_del - Delete IPv4 route entry from switch
3305e8d9049SScott Feldman  *
3315e8d9049SScott Feldman  *	@dst: route's IPv4 destination address
3325e8d9049SScott Feldman  *	@dst_len: destination address length (prefix length)
3335e8d9049SScott Feldman  *	@fi: route FIB info structure
3345e8d9049SScott Feldman  *	@tos: route TOS
3355e8d9049SScott Feldman  *	@type: route type
3365e8d9049SScott Feldman  *	@tb_id: route table ID
3375e8d9049SScott Feldman  *
3385e8d9049SScott Feldman  *	Delete IPv4 route entry from switch device.
3395e8d9049SScott Feldman  */
3405e8d9049SScott Feldman int netdev_switch_fib_ipv4_del(u32 dst, int dst_len, struct fib_info *fi,
3415e8d9049SScott Feldman 			       u8 tos, u8 type, u32 tb_id)
3425e8d9049SScott Feldman {
343*b5d6fbdeSScott Feldman 	struct net_device *dev;
344*b5d6fbdeSScott Feldman 	const struct net_device_ops *ops;
345*b5d6fbdeSScott Feldman 	int err = 0;
346*b5d6fbdeSScott Feldman 
347*b5d6fbdeSScott Feldman 	if (!(fi->fib_flags & RTNH_F_EXTERNAL))
3485e8d9049SScott Feldman 		return 0;
349*b5d6fbdeSScott Feldman 
350*b5d6fbdeSScott Feldman 	dev = netdev_switch_get_dev_by_nhs(fi);
351*b5d6fbdeSScott Feldman 	if (!dev)
352*b5d6fbdeSScott Feldman 		return 0;
353*b5d6fbdeSScott Feldman 	ops = dev->netdev_ops;
354*b5d6fbdeSScott Feldman 
355*b5d6fbdeSScott Feldman 	if (ops->ndo_switch_fib_ipv4_del) {
356*b5d6fbdeSScott Feldman 		err = ops->ndo_switch_fib_ipv4_del(dev, htonl(dst), dst_len,
357*b5d6fbdeSScott Feldman 						   fi, tos, type, tb_id);
358*b5d6fbdeSScott Feldman 		if (!err)
359*b5d6fbdeSScott Feldman 			fi->fib_flags &= ~RTNH_F_EXTERNAL;
360*b5d6fbdeSScott Feldman 	}
361*b5d6fbdeSScott Feldman 
362*b5d6fbdeSScott Feldman 	return err;
3635e8d9049SScott Feldman }
3645e8d9049SScott Feldman EXPORT_SYMBOL(netdev_switch_fib_ipv4_del);
365