linux/net/ipv6/ip6mr.c

// SPDX-License-Identifier: GPL-2.0-or-later
/*
 *	Linux IPv6 multicast routing support for BSD pim6sd
 *	Based on net/ipv4/ipmr.c.
 *
 *	(c) 2004 Mickael Hoerdt, <[email protected]>
 *		LSIIT Laboratory, Strasbourg, France
 *	(c) 2004 Jean-Philippe Andriot, <[email protected]>
 *		6WIND, Paris, France
 *	Copyright (C)2007,2008 USAGI/WIDE Project
 *		YOSHIFUJI Hideaki <[email protected]>
 */

#include <linux/uaccess.h>
#include <linux/types.h>
#include <linux/sched.h>
#include <linux/errno.h>
#include <linux/mm.h>
#include <linux/kernel.h>
#include <linux/fcntl.h>
#include <linux/stat.h>
#include <linux/socket.h>
#include <linux/inet.h>
#include <linux/netdevice.h>
#include <linux/inetdevice.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/init.h>
#include <linux/compat.h>
#include <linux/rhashtable.h>
#include <net/protocol.h>
#include <linux/skbuff.h>
#include <net/raw.h>
#include <linux/notifier.h>
#include <linux/if_arp.h>
#include <net/checksum.h>
#include <net/netlink.h>
#include <net/fib_rules.h>

#include <net/ipv6.h>
#include <net/ip6_route.h>
#include <linux/mroute6.h>
#include <linux/pim.h>
#include <net/addrconf.h>
#include <linux/netfilter_ipv6.h>
#include <linux/export.h>
#include <net/ip6_checksum.h>
#include <linux/netconf.h>
#include <net/ip_tunnels.h>

#include <linux/nospec.h>

struct ip6mr_rule {};

struct ip6mr_result {};

/* Big lock, protecting vif table, mrt cache and mroute socket state.
   Note that the changes are semaphored via rtnl_lock.
 */

static DEFINE_SPINLOCK(mrt_lock);

static struct net_device *vif_dev_read(const struct vif_device *vif)
{}

/* Multicast router control variables */

/* Special spinlock for queue of unresolved entries */
static DEFINE_SPINLOCK(mfc_unres_lock);

/* We return to original Alan's scheme. Hash table of resolved
   entries is changed only in process context and protected
   with weak lock mrt_lock. Queue of unresolved entries is protected
   with strong spinlock mfc_unres_lock.

   In this case data path is free of exclusive locks at all.
 */

static struct kmem_cache *mrt_cachep __read_mostly;

static struct mr_table *ip6mr_new_table(struct net *net, u32 id);
static void ip6mr_free_table(struct mr_table *mrt);

static void ip6_mr_forward(struct net *net, struct mr_table *mrt,
			   struct net_device *dev, struct sk_buff *skb,
			   struct mfc6_cache *cache);
static int ip6mr_cache_report(const struct mr_table *mrt, struct sk_buff *pkt,
			      mifi_t mifi, int assert);
static void mr6_netlink_event(struct mr_table *mrt, struct mfc6_cache *mfc,
			      int cmd);
static void mrt6msg_netlink_event(const struct mr_table *mrt, struct sk_buff *pkt);
static int ip6mr_rtm_getroute(struct sk_buff *in_skb, struct nlmsghdr *nlh,
			      struct netlink_ext_ack *extack);
static int ip6mr_rtm_dumproute(struct sk_buff *skb,
			       struct netlink_callback *cb);
static void mroute_clean_tables(struct mr_table *mrt, int flags);
static void ipmr_expire_process(struct timer_list *t);

#ifdef CONFIG_IPV6_MROUTE_MULTIPLE_TABLES
#define ip6mr_for_each_table(mrt, net)

static struct mr_table *ip6mr_mr_table_iter(struct net *net,
					    struct mr_table *mrt)
{}

static struct mr_table *ip6mr_get_table(struct net *net, u32 id)
{}

static int ip6mr_fib_lookup(struct net *net, struct flowi6 *flp6,
			    struct mr_table **mrt)
{}

static int ip6mr_rule_action(struct fib_rule *rule, struct flowi *flp,
			     int flags, struct fib_lookup_arg *arg)
{}

static int ip6mr_rule_match(struct fib_rule *rule, struct flowi *flp, int flags)
{}

static int ip6mr_rule_configure(struct fib_rule *rule, struct sk_buff *skb,
				struct fib_rule_hdr *frh, struct nlattr **tb,
				struct netlink_ext_ack *extack)
{}

static int ip6mr_rule_compare(struct fib_rule *rule, struct fib_rule_hdr *frh,
			      struct nlattr **tb)
{}

static int ip6mr_rule_fill(struct fib_rule *rule, struct sk_buff *skb,
			   struct fib_rule_hdr *frh)
{}

static const struct fib_rules_ops __net_initconst ip6mr_rules_ops_template =;

static int __net_init ip6mr_rules_init(struct net *net)
{}

static void __net_exit ip6mr_rules_exit(struct net *net)
{}

static int ip6mr_rules_dump(struct net *net, struct notifier_block *nb,
			    struct netlink_ext_ack *extack)
{}

static unsigned int ip6mr_rules_seq_read(struct net *net)
{}

bool ip6mr_rule_default(const struct fib_rule *rule)
{}
EXPORT_SYMBOL();
#else
#define ip6mr_for_each_table

static struct mr_table *ip6mr_mr_table_iter(struct net *net,
					    struct mr_table *mrt)
{
	if (!mrt)
		return net->ipv6.mrt6;
	return NULL;
}

static struct mr_table *ip6mr_get_table(struct net *net, u32 id)
{
	return net->ipv6.mrt6;
}

static int ip6mr_fib_lookup(struct net *net, struct flowi6 *flp6,
			    struct mr_table **mrt)
{
	*mrt = net->ipv6.mrt6;
	return 0;
}

static int __net_init ip6mr_rules_init(struct net *net)
{
	struct mr_table *mrt;

	mrt = ip6mr_new_table(net, RT6_TABLE_DFLT);
	if (IS_ERR(mrt))
		return PTR_ERR(mrt);
	net->ipv6.mrt6 = mrt;
	return 0;
}

static void __net_exit ip6mr_rules_exit(struct net *net)
{
	ASSERT_RTNL();
	ip6mr_free_table(net->ipv6.mrt6);
	net->ipv6.mrt6 = NULL;
}

static int ip6mr_rules_dump(struct net *net, struct notifier_block *nb,
			    struct netlink_ext_ack *extack)
{
	return 0;
}

static unsigned int ip6mr_rules_seq_read(struct net *net)
{
	return 0;
}
#endif

static int ip6mr_hash_cmp(struct rhashtable_compare_arg *arg,
			  const void *ptr)
{}

static const struct rhashtable_params ip6mr_rht_params =;

static void ip6mr_new_table_set(struct mr_table *mrt,
				struct net *net)
{}

static struct mfc6_cache_cmp_arg ip6mr_mr_table_ops_cmparg_any =;

static struct mr_table_ops ip6mr_mr_table_ops =;

static struct mr_table *ip6mr_new_table(struct net *net, u32 id)
{}

static void ip6mr_free_table(struct mr_table *mrt)
{}

#ifdef CONFIG_PROC_FS
/* The /proc interfaces to multicast routing
 * /proc/ip6_mr_cache /proc/ip6_mr_vif
 */

static void *ip6mr_vif_seq_start(struct seq_file *seq, loff_t *pos)
	__acquires(RCU)
{}

static void ip6mr_vif_seq_stop(struct seq_file *seq, void *v)
	__releases(RCU)
{}

static int ip6mr_vif_seq_show(struct seq_file *seq, void *v)
{}

static const struct seq_operations ip6mr_vif_seq_ops =;

static void *ipmr_mfc_seq_start(struct seq_file *seq, loff_t *pos)
{}

static int ipmr_mfc_seq_show(struct seq_file *seq, void *v)
{}

static const struct seq_operations ipmr_mfc_seq_ops =;
#endif

#ifdef CONFIG_IPV6_PIMSM_V2

static int pim6_rcv(struct sk_buff *skb)
{}

static const struct inet6_protocol pim6_protocol =;

/* Service routines creating virtual interfaces: PIMREG */

static netdev_tx_t reg_vif_xmit(struct sk_buff *skb,
				      struct net_device *dev)
{}

static int reg_vif_get_iflink(const struct net_device *dev)
{}

static const struct net_device_ops reg_vif_netdev_ops =;

static void reg_vif_setup(struct net_device *dev)
{}

static struct net_device *ip6mr_reg_vif(struct net *net, struct mr_table *mrt)
{}
#endif

static int call_ip6mr_vif_entry_notifiers(struct net *net,
					  enum fib_event_type event_type,
					  struct vif_device *vif,
					  struct net_device *vif_dev,
					  mifi_t vif_index, u32 tb_id)
{}

static int call_ip6mr_mfc_entry_notifiers(struct net *net,
					  enum fib_event_type event_type,
					  struct mfc6_cache *mfc, u32 tb_id)
{}

/* Delete a VIF entry */
static int mif6_delete(struct mr_table *mrt, int vifi, int notify,
		       struct list_head *head)
{}

static inline void ip6mr_cache_free_rcu(struct rcu_head *head)
{}

static inline void ip6mr_cache_free(struct mfc6_cache *c)
{}

/* Destroy an unresolved cache entry, killing queued skbs
   and reporting error to netlink readers.
 */

static void ip6mr_destroy_unres(struct mr_table *mrt, struct mfc6_cache *c)
{}


/* Timer process for all the unresolved queue. */

static void ipmr_do_expire_process(struct mr_table *mrt)
{}

static void ipmr_expire_process(struct timer_list *t)
{}

/* Fill oifs list. It is called under locked mrt_lock. */

static void ip6mr_update_thresholds(struct mr_table *mrt,
				    struct mr_mfc *cache,
				    unsigned char *ttls)
{}

static int mif6_add(struct net *net, struct mr_table *mrt,
		    struct mif6ctl *vifc, int mrtsock)
{}

static struct mfc6_cache *ip6mr_cache_find(struct mr_table *mrt,
					   const struct in6_addr *origin,
					   const struct in6_addr *mcastgrp)
{}

/* Look for a (*,G) entry */
static struct mfc6_cache *ip6mr_cache_find_any(struct mr_table *mrt,
					       struct in6_addr *mcastgrp,
					       mifi_t mifi)
{}

/* Look for a (S,G,iif) entry if parent != -1 */
static struct mfc6_cache *
ip6mr_cache_find_parent(struct mr_table *mrt,
			const struct in6_addr *origin,
			const struct in6_addr *mcastgrp,
			int parent)
{}

/* Allocate a multicast cache entry */
static struct mfc6_cache *ip6mr_cache_alloc(void)
{}

static struct mfc6_cache *ip6mr_cache_alloc_unres(void)
{}

/*
 *	A cache entry has gone into a resolved state from queued
 */

static void ip6mr_cache_resolve(struct net *net, struct mr_table *mrt,
				struct mfc6_cache *uc, struct mfc6_cache *c)
{}

/*
 *	Bounce a cache query up to pim6sd and netlink.
 *
 *	Called under rcu_read_lock()
 */

static int ip6mr_cache_report(const struct mr_table *mrt, struct sk_buff *pkt,
			      mifi_t mifi, int assert)
{}

/* Queue a packet for resolution. It gets locked cache entry! */
static int ip6mr_cache_unresolved(struct mr_table *mrt, mifi_t mifi,
				  struct sk_buff *skb, struct net_device *dev)
{}

/*
 *	MFC6 cache manipulation by user space
 */

static int ip6mr_mfc_delete(struct mr_table *mrt, struct mf6cctl *mfc,
			    int parent)
{}

static int ip6mr_device_event(struct notifier_block *this,
			      unsigned long event, void *ptr)
{}

static unsigned int ip6mr_seq_read(struct net *net)
{}

static int ip6mr_dump(struct net *net, struct notifier_block *nb,
		      struct netlink_ext_ack *extack)
{}

static struct notifier_block ip6_mr_notifier =;

static const struct fib_notifier_ops ip6mr_notifier_ops_template =;

static int __net_init ip6mr_notifier_init(struct net *net)
{}

static void __net_exit ip6mr_notifier_exit(struct net *net)
{}

/* Setup for IP multicast routing */
static int __net_init ip6mr_net_init(struct net *net)
{}

static void __net_exit ip6mr_net_exit(struct net *net)
{}

static void __net_exit ip6mr_net_exit_batch(struct list_head *net_list)
{}

static struct pernet_operations ip6mr_net_ops =;

int __init ip6_mr_init(void)
{}

void ip6_mr_cleanup(void)
{}

static int ip6mr_mfc_add(struct net *net, struct mr_table *mrt,
			 struct mf6cctl *mfc, int mrtsock, int parent)
{}

/*
 *	Close the multicast socket, and clear the vif tables etc
 */

static void mroute_clean_tables(struct mr_table *mrt, int flags)
{}

static int ip6mr_sk_init(struct mr_table *mrt, struct sock *sk)
{}

int ip6mr_sk_done(struct sock *sk)
{}

bool mroute6_is_socket(struct net *net, struct sk_buff *skb)
{}
EXPORT_SYMBOL();

/*
 *	Socket options and virtual interface manipulation. The whole
 *	virtual interface system is a complete heap, but unfortunately
 *	that's how BSD mrouted happens to think. Maybe one day with a proper
 *	MOSPF/PIM router set up we can clean this up.
 */

int ip6_mroute_setsockopt(struct sock *sk, int optname, sockptr_t optval,
			  unsigned int optlen)
{}

/*
 *	Getsock opt support for the multicast routing system.
 */

int ip6_mroute_getsockopt(struct sock *sk, int optname, sockptr_t optval,
			  sockptr_t optlen)
{}

/*
 *	The IP multicast ioctl support routines.
 */
int ip6mr_ioctl(struct sock *sk, int cmd, void *arg)
{}

#ifdef CONFIG_COMPAT
struct compat_sioc_sg_req6 {};

struct compat_sioc_mif_req6 {};

int ip6mr_compat_ioctl(struct sock *sk, unsigned int cmd, void __user *arg)
{}
#endif

static inline int ip6mr_forward2_finish(struct net *net, struct sock *sk, struct sk_buff *skb)
{}

/*
 *	Processing handlers for ip6mr_forward
 */

static int ip6mr_forward2(struct net *net, struct mr_table *mrt,
			  struct sk_buff *skb, int vifi)
{}

/* Called with rcu_read_lock() */
static int ip6mr_find_vif(struct mr_table *mrt, struct net_device *dev)
{}

/* Called under rcu_read_lock() */
static void ip6_mr_forward(struct net *net, struct mr_table *mrt,
			   struct net_device *dev, struct sk_buff *skb,
			   struct mfc6_cache *c)
{}


/*
 *	Multicast packets for forwarding arrive here
 */

int ip6_mr_input(struct sk_buff *skb)
{}

int ip6mr_get_route(struct net *net, struct sk_buff *skb, struct rtmsg *rtm,
		    u32 portid)
{}

static int ip6mr_fill_mroute(struct mr_table *mrt, struct sk_buff *skb,
			     u32 portid, u32 seq, struct mfc6_cache *c, int cmd,
			     int flags)
{}

static int _ip6mr_fill_mroute(struct mr_table *mrt, struct sk_buff *skb,
			      u32 portid, u32 seq, struct mr_mfc *c,
			      int cmd, int flags)
{}

static int mr6_msgsize(bool unresolved, int maxvif)
{}

static void mr6_netlink_event(struct mr_table *mrt, struct mfc6_cache *mfc,
			      int cmd)
{}

static size_t mrt6msg_netlink_msgsize(size_t payloadlen)
{}

static void mrt6msg_netlink_event(const struct mr_table *mrt, struct sk_buff *pkt)
{}

static const struct nla_policy ip6mr_getroute_policy[RTA_MAX + 1] =;

static int ip6mr_rtm_valid_getroute_req(struct sk_buff *skb,
					const struct nlmsghdr *nlh,
					struct nlattr **tb,
					struct netlink_ext_ack *extack)
{}

static int ip6mr_rtm_getroute(struct sk_buff *in_skb, struct nlmsghdr *nlh,
			      struct netlink_ext_ack *extack)
{}

static int ip6mr_rtm_dumproute(struct sk_buff *skb, struct netlink_callback *cb)
{}