| #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 | |
| * @dev_tracker: refcount tracker for @dev reference | |
| * @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 __rcu *dev; | |
| netdevice_tracker dev_tracker; | |
| 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, | |
| struct net_device *vif_dev, | |
| 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, | |
| struct net_device *vif_dev, | |
| 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 | |
| /* Note: This helper is deprecated. */ | |
| #define VIF_EXISTS(_mrt, _idx) (!!rcu_access_pointer((_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; | |
| atomic_long_t bytes; | |
| atomic_long_t pkt; | |
| atomic_long_t 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; | |
| }; | |
| static inline bool mr_can_free_table(struct net *net) | |
| { | |
| return !check_net(net) || !net_initialized(net); | |
| } | |
| #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), | |
| 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), | |
| 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 |