|  | #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 |