| #ifndef __LINUX_MROUTE_BASE_H | 
 | #define __LINUX_MROUTE_BASE_H | 
 |  | 
 | #include <linux/netdevice.h> | 
 | #include <linux/rhashtable-types.h> | 
 | #include <linux/spinlock.h> | 
 | #include <net/net_namespace.h> | 
 | #include <net/sock.h> | 
 | #include <net/fib_notifier.h> | 
 | #include <net/ip_fib.h> | 
 |  | 
 | /** | 
 |  * struct vif_device - interface representor for multicast routing | 
 |  * @dev: network device being used | 
 |  * @bytes_in: statistic; bytes ingressing | 
 |  * @bytes_out: statistic; bytes egresing | 
 |  * @pkt_in: statistic; packets ingressing | 
 |  * @pkt_out: statistic; packets egressing | 
 |  * @rate_limit: Traffic shaping (NI) | 
 |  * @threshold: TTL threshold | 
 |  * @flags: Control flags | 
 |  * @link: Physical interface index | 
 |  * @dev_parent_id: device parent id | 
 |  * @local: Local address | 
 |  * @remote: Remote address for tunnels | 
 |  */ | 
 | struct vif_device { | 
 | 	struct net_device *dev; | 
 | 	unsigned long bytes_in, bytes_out; | 
 | 	unsigned long pkt_in, pkt_out; | 
 | 	unsigned long rate_limit; | 
 | 	unsigned char threshold; | 
 | 	unsigned short flags; | 
 | 	int link; | 
 |  | 
 | 	/* Currently only used by ipmr */ | 
 | 	struct netdev_phys_item_id dev_parent_id; | 
 | 	__be32 local, remote; | 
 | }; | 
 |  | 
 | struct vif_entry_notifier_info { | 
 | 	struct fib_notifier_info info; | 
 | 	struct net_device *dev; | 
 | 	unsigned short vif_index; | 
 | 	unsigned short vif_flags; | 
 | 	u32 tb_id; | 
 | }; | 
 |  | 
 | static inline int mr_call_vif_notifier(struct notifier_block *nb, | 
 | 				       unsigned short family, | 
 | 				       enum fib_event_type event_type, | 
 | 				       struct vif_device *vif, | 
 | 				       unsigned short vif_index, u32 tb_id, | 
 | 				       struct netlink_ext_ack *extack) | 
 | { | 
 | 	struct vif_entry_notifier_info info = { | 
 | 		.info = { | 
 | 			.family = family, | 
 | 			.extack = extack, | 
 | 		}, | 
 | 		.dev = vif->dev, | 
 | 		.vif_index = vif_index, | 
 | 		.vif_flags = vif->flags, | 
 | 		.tb_id = tb_id, | 
 | 	}; | 
 |  | 
 | 	return call_fib_notifier(nb, event_type, &info.info); | 
 | } | 
 |  | 
 | static inline int mr_call_vif_notifiers(struct net *net, | 
 | 					unsigned short family, | 
 | 					enum fib_event_type event_type, | 
 | 					struct vif_device *vif, | 
 | 					unsigned short vif_index, u32 tb_id, | 
 | 					unsigned int *ipmr_seq) | 
 | { | 
 | 	struct vif_entry_notifier_info info = { | 
 | 		.info = { | 
 | 			.family = family, | 
 | 		}, | 
 | 		.dev = vif->dev, | 
 | 		.vif_index = vif_index, | 
 | 		.vif_flags = vif->flags, | 
 | 		.tb_id = tb_id, | 
 | 	}; | 
 |  | 
 | 	ASSERT_RTNL(); | 
 | 	(*ipmr_seq)++; | 
 | 	return call_fib_notifiers(net, event_type, &info.info); | 
 | } | 
 |  | 
 | #ifndef MAXVIFS | 
 | /* This one is nasty; value is defined in uapi using different symbols for | 
 |  * mroute and morute6 but both map into same 32. | 
 |  */ | 
 | #define MAXVIFS	32 | 
 | #endif | 
 |  | 
 | #define VIF_EXISTS(_mrt, _idx) (!!((_mrt)->vif_table[_idx].dev)) | 
 |  | 
 | /* mfc_flags: | 
 |  * MFC_STATIC - the entry was added statically (not by a routing daemon) | 
 |  * MFC_OFFLOAD - the entry was offloaded to the hardware | 
 |  */ | 
 | enum { | 
 | 	MFC_STATIC = BIT(0), | 
 | 	MFC_OFFLOAD = BIT(1), | 
 | }; | 
 |  | 
 | /** | 
 |  * struct mr_mfc - common multicast routing entries | 
 |  * @mnode: rhashtable list | 
 |  * @mfc_parent: source interface (iif) | 
 |  * @mfc_flags: entry flags | 
 |  * @expires: unresolved entry expire time | 
 |  * @unresolved: unresolved cached skbs | 
 |  * @last_assert: time of last assert | 
 |  * @minvif: minimum VIF id | 
 |  * @maxvif: maximum VIF id | 
 |  * @bytes: bytes that have passed for this entry | 
 |  * @pkt: packets that have passed for this entry | 
 |  * @wrong_if: number of wrong source interface hits | 
 |  * @lastuse: time of last use of the group (traffic or update) | 
 |  * @ttls: OIF TTL threshold array | 
 |  * @refcount: reference count for this entry | 
 |  * @list: global entry list | 
 |  * @rcu: used for entry destruction | 
 |  * @free: Operation used for freeing an entry under RCU | 
 |  */ | 
 | struct mr_mfc { | 
 | 	struct rhlist_head mnode; | 
 | 	unsigned short mfc_parent; | 
 | 	int mfc_flags; | 
 |  | 
 | 	union { | 
 | 		struct { | 
 | 			unsigned long expires; | 
 | 			struct sk_buff_head unresolved; | 
 | 		} unres; | 
 | 		struct { | 
 | 			unsigned long last_assert; | 
 | 			int minvif; | 
 | 			int maxvif; | 
 | 			unsigned long bytes; | 
 | 			unsigned long pkt; | 
 | 			unsigned long wrong_if; | 
 | 			unsigned long lastuse; | 
 | 			unsigned char ttls[MAXVIFS]; | 
 | 			refcount_t refcount; | 
 | 		} res; | 
 | 	} mfc_un; | 
 | 	struct list_head list; | 
 | 	struct rcu_head	rcu; | 
 | 	void (*free)(struct rcu_head *head); | 
 | }; | 
 |  | 
 | static inline void mr_cache_put(struct mr_mfc *c) | 
 | { | 
 | 	if (refcount_dec_and_test(&c->mfc_un.res.refcount)) | 
 | 		call_rcu(&c->rcu, c->free); | 
 | } | 
 |  | 
 | static inline void mr_cache_hold(struct mr_mfc *c) | 
 | { | 
 | 	refcount_inc(&c->mfc_un.res.refcount); | 
 | } | 
 |  | 
 | struct mfc_entry_notifier_info { | 
 | 	struct fib_notifier_info info; | 
 | 	struct mr_mfc *mfc; | 
 | 	u32 tb_id; | 
 | }; | 
 |  | 
 | static inline int mr_call_mfc_notifier(struct notifier_block *nb, | 
 | 				       unsigned short family, | 
 | 				       enum fib_event_type event_type, | 
 | 				       struct mr_mfc *mfc, u32 tb_id, | 
 | 				       struct netlink_ext_ack *extack) | 
 | { | 
 | 	struct mfc_entry_notifier_info info = { | 
 | 		.info = { | 
 | 			.family = family, | 
 | 			.extack = extack, | 
 | 		}, | 
 | 		.mfc = mfc, | 
 | 		.tb_id = tb_id | 
 | 	}; | 
 |  | 
 | 	return call_fib_notifier(nb, event_type, &info.info); | 
 | } | 
 |  | 
 | static inline int mr_call_mfc_notifiers(struct net *net, | 
 | 					unsigned short family, | 
 | 					enum fib_event_type event_type, | 
 | 					struct mr_mfc *mfc, u32 tb_id, | 
 | 					unsigned int *ipmr_seq) | 
 | { | 
 | 	struct mfc_entry_notifier_info info = { | 
 | 		.info = { | 
 | 			.family = family, | 
 | 		}, | 
 | 		.mfc = mfc, | 
 | 		.tb_id = tb_id | 
 | 	}; | 
 |  | 
 | 	ASSERT_RTNL(); | 
 | 	(*ipmr_seq)++; | 
 | 	return call_fib_notifiers(net, event_type, &info.info); | 
 | } | 
 |  | 
 | struct mr_table; | 
 |  | 
 | /** | 
 |  * struct mr_table_ops - callbacks and info for protocol-specific ops | 
 |  * @rht_params: parameters for accessing the MFC hash | 
 |  * @cmparg_any: a hash key to be used for matching on (*,*) routes | 
 |  */ | 
 | struct mr_table_ops { | 
 | 	const struct rhashtable_params *rht_params; | 
 | 	void *cmparg_any; | 
 | }; | 
 |  | 
 | /** | 
 |  * struct mr_table - a multicast routing table | 
 |  * @list: entry within a list of multicast routing tables | 
 |  * @net: net where this table belongs | 
 |  * @ops: protocol specific operations | 
 |  * @id: identifier of the table | 
 |  * @mroute_sk: socket associated with the table | 
 |  * @ipmr_expire_timer: timer for handling unresolved routes | 
 |  * @mfc_unres_queue: list of unresolved MFC entries | 
 |  * @vif_table: array containing all possible vifs | 
 |  * @mfc_hash: Hash table of all resolved routes for easy lookup | 
 |  * @mfc_cache_list: list of resovled routes for possible traversal | 
 |  * @maxvif: Identifier of highest value vif currently in use | 
 |  * @cache_resolve_queue_len: current size of unresolved queue | 
 |  * @mroute_do_assert: Whether to inform userspace on wrong ingress | 
 |  * @mroute_do_pim: Whether to receive IGMP PIMv1 | 
 |  * @mroute_reg_vif_num: PIM-device vif index | 
 |  */ | 
 | struct mr_table { | 
 | 	struct list_head	list; | 
 | 	possible_net_t		net; | 
 | 	struct mr_table_ops	ops; | 
 | 	u32			id; | 
 | 	struct sock __rcu	*mroute_sk; | 
 | 	struct timer_list	ipmr_expire_timer; | 
 | 	struct list_head	mfc_unres_queue; | 
 | 	struct vif_device	vif_table[MAXVIFS]; | 
 | 	struct rhltable		mfc_hash; | 
 | 	struct list_head	mfc_cache_list; | 
 | 	int			maxvif; | 
 | 	atomic_t		cache_resolve_queue_len; | 
 | 	bool			mroute_do_assert; | 
 | 	bool			mroute_do_pim; | 
 | 	bool			mroute_do_wrvifwhole; | 
 | 	int			mroute_reg_vif_num; | 
 | }; | 
 |  | 
 | #ifdef CONFIG_IP_MROUTE_COMMON | 
 | void vif_device_init(struct vif_device *v, | 
 | 		     struct net_device *dev, | 
 | 		     unsigned long rate_limit, | 
 | 		     unsigned char threshold, | 
 | 		     unsigned short flags, | 
 | 		     unsigned short get_iflink_mask); | 
 |  | 
 | struct mr_table * | 
 | mr_table_alloc(struct net *net, u32 id, | 
 | 	       struct mr_table_ops *ops, | 
 | 	       void (*expire_func)(struct timer_list *t), | 
 | 	       void (*table_set)(struct mr_table *mrt, | 
 | 				 struct net *net)); | 
 |  | 
 | /* These actually return 'struct mr_mfc *', but to avoid need for explicit | 
 |  * castings they simply return void. | 
 |  */ | 
 | void *mr_mfc_find_parent(struct mr_table *mrt, | 
 | 			 void *hasharg, int parent); | 
 | void *mr_mfc_find_any_parent(struct mr_table *mrt, int vifi); | 
 | void *mr_mfc_find_any(struct mr_table *mrt, int vifi, void *hasharg); | 
 |  | 
 | int mr_fill_mroute(struct mr_table *mrt, struct sk_buff *skb, | 
 | 		   struct mr_mfc *c, struct rtmsg *rtm); | 
 | int mr_table_dump(struct mr_table *mrt, struct sk_buff *skb, | 
 | 		  struct netlink_callback *cb, | 
 | 		  int (*fill)(struct mr_table *mrt, struct sk_buff *skb, | 
 | 			      u32 portid, u32 seq, struct mr_mfc *c, | 
 | 			      int cmd, int flags), | 
 | 		  spinlock_t *lock, struct fib_dump_filter *filter); | 
 | int mr_rtm_dumproute(struct sk_buff *skb, struct netlink_callback *cb, | 
 | 		     struct mr_table *(*iter)(struct net *net, | 
 | 					      struct mr_table *mrt), | 
 | 		     int (*fill)(struct mr_table *mrt, | 
 | 				 struct sk_buff *skb, | 
 | 				 u32 portid, u32 seq, struct mr_mfc *c, | 
 | 				 int cmd, int flags), | 
 | 		     spinlock_t *lock, struct fib_dump_filter *filter); | 
 |  | 
 | int mr_dump(struct net *net, struct notifier_block *nb, unsigned short family, | 
 | 	    int (*rules_dump)(struct net *net, | 
 | 			      struct notifier_block *nb, | 
 | 			      struct netlink_ext_ack *extack), | 
 | 	    struct mr_table *(*mr_iter)(struct net *net, | 
 | 					struct mr_table *mrt), | 
 | 	    rwlock_t *mrt_lock, struct netlink_ext_ack *extack); | 
 | #else | 
 | static inline void vif_device_init(struct vif_device *v, | 
 | 				   struct net_device *dev, | 
 | 				   unsigned long rate_limit, | 
 | 				   unsigned char threshold, | 
 | 				   unsigned short flags, | 
 | 				   unsigned short get_iflink_mask) | 
 | { | 
 | } | 
 |  | 
 | static inline void *mr_mfc_find_parent(struct mr_table *mrt, | 
 | 				       void *hasharg, int parent) | 
 | { | 
 | 	return NULL; | 
 | } | 
 |  | 
 | static inline void *mr_mfc_find_any_parent(struct mr_table *mrt, | 
 | 					   int vifi) | 
 | { | 
 | 	return NULL; | 
 | } | 
 |  | 
 | static inline struct mr_mfc *mr_mfc_find_any(struct mr_table *mrt, | 
 | 					     int vifi, void *hasharg) | 
 | { | 
 | 	return NULL; | 
 | } | 
 |  | 
 | static inline int mr_fill_mroute(struct mr_table *mrt, struct sk_buff *skb, | 
 | 				 struct mr_mfc *c, struct rtmsg *rtm) | 
 | { | 
 | 	return -EINVAL; | 
 | } | 
 |  | 
 | static inline int | 
 | mr_rtm_dumproute(struct sk_buff *skb, struct netlink_callback *cb, | 
 | 		 struct mr_table *(*iter)(struct net *net, | 
 | 					  struct mr_table *mrt), | 
 | 		 int (*fill)(struct mr_table *mrt, | 
 | 			     struct sk_buff *skb, | 
 | 			     u32 portid, u32 seq, struct mr_mfc *c, | 
 | 			     int cmd, int flags), | 
 | 		 spinlock_t *lock, struct fib_dump_filter *filter) | 
 | { | 
 | 	return -EINVAL; | 
 | } | 
 |  | 
 | static inline int mr_dump(struct net *net, struct notifier_block *nb, | 
 | 			  unsigned short family, | 
 | 			  int (*rules_dump)(struct net *net, | 
 | 					    struct notifier_block *nb, | 
 | 					    struct netlink_ext_ack *extack), | 
 | 			  struct mr_table *(*mr_iter)(struct net *net, | 
 | 						      struct mr_table *mrt), | 
 | 			  rwlock_t *mrt_lock, struct netlink_ext_ack *extack) | 
 | { | 
 | 	return -EINVAL; | 
 | } | 
 | #endif | 
 |  | 
 | static inline void *mr_mfc_find(struct mr_table *mrt, void *hasharg) | 
 | { | 
 | 	return mr_mfc_find_parent(mrt, hasharg, -1); | 
 | } | 
 |  | 
 | #ifdef CONFIG_PROC_FS | 
 | struct mr_vif_iter { | 
 | 	struct seq_net_private p; | 
 | 	struct mr_table *mrt; | 
 | 	int ct; | 
 | }; | 
 |  | 
 | struct mr_mfc_iter { | 
 | 	struct seq_net_private p; | 
 | 	struct mr_table *mrt; | 
 | 	struct list_head *cache; | 
 |  | 
 | 	/* Lock protecting the mr_table's unresolved queue */ | 
 | 	spinlock_t *lock; | 
 | }; | 
 |  | 
 | #ifdef CONFIG_IP_MROUTE_COMMON | 
 | void *mr_vif_seq_idx(struct net *net, struct mr_vif_iter *iter, loff_t pos); | 
 | void *mr_vif_seq_next(struct seq_file *seq, void *v, loff_t *pos); | 
 |  | 
 | static inline void *mr_vif_seq_start(struct seq_file *seq, loff_t *pos) | 
 | { | 
 | 	return *pos ? mr_vif_seq_idx(seq_file_net(seq), | 
 | 				     seq->private, *pos - 1) | 
 | 		    : SEQ_START_TOKEN; | 
 | } | 
 |  | 
 | /* These actually return 'struct mr_mfc *', but to avoid need for explicit | 
 |  * castings they simply return void. | 
 |  */ | 
 | void *mr_mfc_seq_idx(struct net *net, | 
 | 		     struct mr_mfc_iter *it, loff_t pos); | 
 | void *mr_mfc_seq_next(struct seq_file *seq, void *v, | 
 | 		      loff_t *pos); | 
 |  | 
 | static inline void *mr_mfc_seq_start(struct seq_file *seq, loff_t *pos, | 
 | 				     struct mr_table *mrt, spinlock_t *lock) | 
 | { | 
 | 	struct mr_mfc_iter *it = seq->private; | 
 |  | 
 | 	it->mrt = mrt; | 
 | 	it->cache = NULL; | 
 | 	it->lock = lock; | 
 |  | 
 | 	return *pos ? mr_mfc_seq_idx(seq_file_net(seq), | 
 | 				     seq->private, *pos - 1) | 
 | 		    : SEQ_START_TOKEN; | 
 | } | 
 |  | 
 | static inline void mr_mfc_seq_stop(struct seq_file *seq, void *v) | 
 | { | 
 | 	struct mr_mfc_iter *it = seq->private; | 
 | 	struct mr_table *mrt = it->mrt; | 
 |  | 
 | 	if (it->cache == &mrt->mfc_unres_queue) | 
 | 		spin_unlock_bh(it->lock); | 
 | 	else if (it->cache == &mrt->mfc_cache_list) | 
 | 		rcu_read_unlock(); | 
 | } | 
 | #else | 
 | static inline void *mr_vif_seq_idx(struct net *net, struct mr_vif_iter *iter, | 
 | 				   loff_t pos) | 
 | { | 
 | 	return NULL; | 
 | } | 
 |  | 
 | static inline void *mr_vif_seq_next(struct seq_file *seq, | 
 | 				    void *v, loff_t *pos) | 
 | { | 
 | 	return NULL; | 
 | } | 
 |  | 
 | static inline void *mr_vif_seq_start(struct seq_file *seq, loff_t *pos) | 
 | { | 
 | 	return NULL; | 
 | } | 
 |  | 
 | static inline void *mr_mfc_seq_idx(struct net *net, | 
 | 				   struct mr_mfc_iter *it, loff_t pos) | 
 | { | 
 | 	return NULL; | 
 | } | 
 |  | 
 | static inline void *mr_mfc_seq_next(struct seq_file *seq, void *v, | 
 | 				    loff_t *pos) | 
 | { | 
 | 	return NULL; | 
 | } | 
 |  | 
 | static inline void *mr_mfc_seq_start(struct seq_file *seq, loff_t *pos, | 
 | 				     struct mr_table *mrt, spinlock_t *lock) | 
 | { | 
 | 	return NULL; | 
 | } | 
 |  | 
 | static inline void mr_mfc_seq_stop(struct seq_file *seq, void *v) | 
 | { | 
 | } | 
 | #endif | 
 | #endif | 
 | #endif |