linux/net/ipv6/mcast.c

// SPDX-License-Identifier: GPL-2.0-or-later
/*
 *	Multicast support for IPv6
 *	Linux INET6 implementation
 *
 *	Authors:
 *	Pedro Roque		<[email protected]>
 *
 *	Based on linux/ipv4/igmp.c and linux/ipv4/ip_sockglue.c
 */

/* Changes:
 *
 *	yoshfuji	: fix format of router-alert option
 *	YOSHIFUJI Hideaki @USAGI:
 *		Fixed source address for MLD message based on
 *		<draft-ietf-magma-mld-source-05.txt>.
 *	YOSHIFUJI Hideaki @USAGI:
 *		- Ignore Queries for invalid addresses.
 *		- MLD for link-local addresses.
 *	David L Stevens <[email protected]>:
 *		- MLDv2 support
 */

#include <linux/module.h>
#include <linux/errno.h>
#include <linux/types.h>
#include <linux/string.h>
#include <linux/socket.h>
#include <linux/sockios.h>
#include <linux/jiffies.h>
#include <linux/net.h>
#include <linux/in.h>
#include <linux/in6.h>
#include <linux/netdevice.h>
#include <linux/if_arp.h>
#include <linux/route.h>
#include <linux/init.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/slab.h>
#include <linux/pkt_sched.h>
#include <net/mld.h>
#include <linux/workqueue.h>

#include <linux/netfilter.h>
#include <linux/netfilter_ipv6.h>

#include <net/net_namespace.h>
#include <net/sock.h>
#include <net/snmp.h>

#include <net/ipv6.h>
#include <net/protocol.h>
#include <net/if_inet6.h>
#include <net/ndisc.h>
#include <net/addrconf.h>
#include <net/ip6_route.h>
#include <net/inet_common.h>

#include <net/ip6_checksum.h>

/* Ensure that we have struct in6_addr aligned on 32bit word. */
static int __mld2_query_bugs[] __attribute__((__unused__)) =;

static struct workqueue_struct *mld_wq;
static struct in6_addr mld2_all_mcr =;

static void igmp6_join_group(struct ifmcaddr6 *ma);
static void igmp6_leave_group(struct ifmcaddr6 *ma);
static void mld_mca_work(struct work_struct *work);

static void mld_ifc_event(struct inet6_dev *idev);
static bool mld_in_v1_mode(const struct inet6_dev *idev);
static int sf_setstate(struct ifmcaddr6 *pmc);
static void sf_markstate(struct ifmcaddr6 *pmc);
static void ip6_mc_clear_src(struct ifmcaddr6 *pmc);
static int ip6_mc_del_src(struct inet6_dev *idev, const struct in6_addr *pmca,
			  int sfmode, int sfcount, const struct in6_addr *psfsrc,
			  int delta);
static int ip6_mc_add_src(struct inet6_dev *idev, const struct in6_addr *pmca,
			  int sfmode, int sfcount, const struct in6_addr *psfsrc,
			  int delta);
static int ip6_mc_leave_src(struct sock *sk, struct ipv6_mc_socklist *iml,
			    struct inet6_dev *idev);
static int __ipv6_dev_mc_inc(struct net_device *dev,
			     const struct in6_addr *addr, unsigned int mode);

#define MLD_QRV_DEFAULT
/* RFC3810, 9.2. Query Interval */
#define MLD_QI_DEFAULT
/* RFC3810, 9.3. Query Response Interval */
#define MLD_QRI_DEFAULT

/* RFC3810, 8.1 Query Version Distinctions */
#define MLD_V1_QUERY_LEN
#define MLD_V2_QUERY_LEN_MIN

#define IPV6_MLD_MAX_MSF

int sysctl_mld_max_msf __read_mostly =;
int sysctl_mld_qrv __read_mostly =;

/*
 *	socket join on multicast group
 */
#define mc_dereference(e, idev)

#define sock_dereference(e, sk)

#define for_each_pmc_socklock(np, sk, pmc)

#define for_each_pmc_rcu(np, pmc)

#define for_each_psf_mclock(mc, psf)

#define for_each_psf_rcu(mc, psf)

#define for_each_psf_tomb(mc, psf)

#define for_each_mc_mclock(idev, mc)

#define for_each_mc_rcu(idev, mc)

#define for_each_mc_tomb(idev, mc)

static int unsolicited_report_interval(struct inet6_dev *idev)
{}

static int __ipv6_sock_mc_join(struct sock *sk, int ifindex,
			       const struct in6_addr *addr, unsigned int mode)
{}

int ipv6_sock_mc_join(struct sock *sk, int ifindex, const struct in6_addr *addr)
{}
EXPORT_SYMBOL();

int ipv6_sock_mc_join_ssm(struct sock *sk, int ifindex,
			  const struct in6_addr *addr, unsigned int mode)
{}

/*
 *	socket leave on multicast group
 */
int ipv6_sock_mc_drop(struct sock *sk, int ifindex, const struct in6_addr *addr)
{}
EXPORT_SYMBOL();

static struct inet6_dev *ip6_mc_find_dev_rtnl(struct net *net,
					      const struct in6_addr *group,
					      int ifindex)
{}

void __ipv6_sock_mc_close(struct sock *sk)
{}

void ipv6_sock_mc_close(struct sock *sk)
{}

int ip6_mc_source(int add, int omode, struct sock *sk,
	struct group_source_req *pgsr)
{}

int ip6_mc_msfilter(struct sock *sk, struct group_filter *gsf,
		    struct sockaddr_storage *list)
{}

int ip6_mc_msfget(struct sock *sk, struct group_filter *gsf,
		  sockptr_t optval, size_t ss_offset)
{}

bool inet6_mc_check(const struct sock *sk, const struct in6_addr *mc_addr,
		    const struct in6_addr *src_addr)
{}

/* called with mc_lock */
static void igmp6_group_added(struct ifmcaddr6 *mc)
{}

/* called with mc_lock */
static void igmp6_group_dropped(struct ifmcaddr6 *mc)
{}

/*
 * deleted ifmcaddr6 manipulation
 * called with mc_lock
 */
static void mld_add_delrec(struct inet6_dev *idev, struct ifmcaddr6 *im)
{}

/* called with mc_lock */
static void mld_del_delrec(struct inet6_dev *idev, struct ifmcaddr6 *im)
{}

/* called with mc_lock */
static void mld_clear_delrec(struct inet6_dev *idev)
{}

static void mld_clear_query(struct inet6_dev *idev)
{}

static void mld_clear_report(struct inet6_dev *idev)
{}

static void mca_get(struct ifmcaddr6 *mc)
{}

static void ma_put(struct ifmcaddr6 *mc)
{}

/* called with mc_lock */
static struct ifmcaddr6 *mca_alloc(struct inet6_dev *idev,
				   const struct in6_addr *addr,
				   unsigned int mode)
{}

/*
 *	device multicast group inc (add if not found)
 */
static int __ipv6_dev_mc_inc(struct net_device *dev,
			     const struct in6_addr *addr, unsigned int mode)
{}

int ipv6_dev_mc_inc(struct net_device *dev, const struct in6_addr *addr)
{}
EXPORT_SYMBOL();

/*
 * device multicast group del
 */
int __ipv6_dev_mc_dec(struct inet6_dev *idev, const struct in6_addr *addr)
{}

int ipv6_dev_mc_dec(struct net_device *dev, const struct in6_addr *addr)
{}
EXPORT_SYMBOL();

/*
 *	check if the interface/address pair is valid
 */
bool ipv6_chk_mcast_addr(struct net_device *dev, const struct in6_addr *group,
			 const struct in6_addr *src_addr)
{}

/* called with mc_lock */
static void mld_gq_start_work(struct inet6_dev *idev)
{}

/* called with mc_lock */
static void mld_gq_stop_work(struct inet6_dev *idev)
{}

/* called with mc_lock */
static void mld_ifc_start_work(struct inet6_dev *idev, unsigned long delay)
{}

/* called with mc_lock */
static void mld_ifc_stop_work(struct inet6_dev *idev)
{}

/* called with mc_lock */
static void mld_dad_start_work(struct inet6_dev *idev, unsigned long delay)
{}

static void mld_dad_stop_work(struct inet6_dev *idev)
{}

static void mld_query_stop_work(struct inet6_dev *idev)
{}

static void mld_report_stop_work(struct inet6_dev *idev)
{}

/*
 * IGMP handling (alias multicast ICMPv6 messages)
 * called with mc_lock
 */
static void igmp6_group_queried(struct ifmcaddr6 *ma, unsigned long resptime)
{}

/* mark EXCLUDE-mode sources
 * called with mc_lock
 */
static bool mld_xmarksources(struct ifmcaddr6 *pmc, int nsrcs,
			     const struct in6_addr *srcs)
{}

/* called with mc_lock */
static bool mld_marksources(struct ifmcaddr6 *pmc, int nsrcs,
			    const struct in6_addr *srcs)
{}

static int mld_force_mld_version(const struct inet6_dev *idev)
{}

static bool mld_in_v2_mode_only(const struct inet6_dev *idev)
{}

static bool mld_in_v1_mode_only(const struct inet6_dev *idev)
{}

static bool mld_in_v1_mode(const struct inet6_dev *idev)
{}

static void mld_set_v1_mode(struct inet6_dev *idev)
{}

static void mld_update_qrv(struct inet6_dev *idev,
			   const struct mld2_query *mlh2)
{}

static void mld_update_qi(struct inet6_dev *idev,
			  const struct mld2_query *mlh2)
{}

static void mld_update_qri(struct inet6_dev *idev,
			   const struct mld2_query *mlh2)
{}

static int mld_process_v1(struct inet6_dev *idev, struct mld_msg *mld,
			  unsigned long *max_delay, bool v1_query)
{}

static void mld_process_v2(struct inet6_dev *idev, struct mld2_query *mld,
			   unsigned long *max_delay)
{}

/* called with rcu_read_lock() */
void igmp6_event_query(struct sk_buff *skb)
{}

static void __mld_query_work(struct sk_buff *skb)
{}

static void mld_query_work(struct work_struct *work)
{}

/* called with rcu_read_lock() */
void igmp6_event_report(struct sk_buff *skb)
{}

static void __mld_report_work(struct sk_buff *skb)
{}

static void mld_report_work(struct work_struct *work)
{}

static bool is_in(struct ifmcaddr6 *pmc, struct ip6_sf_list *psf, int type,
		  int gdeleted, int sdeleted)
{}

static int
mld_scount(struct ifmcaddr6 *pmc, int type, int gdeleted, int sdeleted)
{}

static void ip6_mc_hdr(const struct sock *sk, struct sk_buff *skb,
		       struct net_device *dev, const struct in6_addr *saddr,
		       const struct in6_addr *daddr, int proto, int len)
{}

static struct sk_buff *mld_newpack(struct inet6_dev *idev, unsigned int mtu)
{}

static void mld_sendpack(struct sk_buff *skb)
{}

static int grec_size(struct ifmcaddr6 *pmc, int type, int gdel, int sdel)
{}

static struct sk_buff *add_grhead(struct sk_buff *skb, struct ifmcaddr6 *pmc,
	int type, struct mld2_grec **ppgr, unsigned int mtu)
{}

#define AVAILABLE(skb)

/* called with mc_lock */
static struct sk_buff *add_grec(struct sk_buff *skb, struct ifmcaddr6 *pmc,
				int type, int gdeleted, int sdeleted,
				int crsend)
{}

/* called with mc_lock */
static void mld_send_report(struct inet6_dev *idev, struct ifmcaddr6 *pmc)
{}

/*
 * remove zero-count source records from a source filter list
 * called with mc_lock
 */
static void mld_clear_zeros(struct ip6_sf_list __rcu **ppsf, struct inet6_dev *idev)
{}

/* called with mc_lock */
static void mld_send_cr(struct inet6_dev *idev)
{}

static void igmp6_send(struct in6_addr *addr, struct net_device *dev, int type)
{}

/* called with mc_lock */
static void mld_send_initial_cr(struct inet6_dev *idev)
{}

void ipv6_mc_dad_complete(struct inet6_dev *idev)
{}

static void mld_dad_work(struct work_struct *work)
{}

/* called with mc_lock */
static int ip6_mc_del1_src(struct ifmcaddr6 *pmc, int sfmode,
	const struct in6_addr *psfsrc)
{}

/* called with mc_lock */
static int ip6_mc_del_src(struct inet6_dev *idev, const struct in6_addr *pmca,
			  int sfmode, int sfcount, const struct in6_addr *psfsrc,
			  int delta)
{}

/*
 * Add multicast single-source filter to the interface list
 * called with mc_lock
 */
static int ip6_mc_add1_src(struct ifmcaddr6 *pmc, int sfmode,
	const struct in6_addr *psfsrc)
{}

/* called with mc_lock */
static void sf_markstate(struct ifmcaddr6 *pmc)
{}

/* called with mc_lock */
static int sf_setstate(struct ifmcaddr6 *pmc)
{}

/*
 * Add multicast source filter list to the interface list
 * called with mc_lock
 */
static int ip6_mc_add_src(struct inet6_dev *idev, const struct in6_addr *pmca,
			  int sfmode, int sfcount, const struct in6_addr *psfsrc,
			  int delta)
{}

/* called with mc_lock */
static void ip6_mc_clear_src(struct ifmcaddr6 *pmc)
{}

/* called with mc_lock */
static void igmp6_join_group(struct ifmcaddr6 *ma)
{}

static int ip6_mc_leave_src(struct sock *sk, struct ipv6_mc_socklist *iml,
			    struct inet6_dev *idev)
{}

/* called with mc_lock */
static void igmp6_leave_group(struct ifmcaddr6 *ma)
{}

static void mld_gq_work(struct work_struct *work)
{}

static void mld_ifc_work(struct work_struct *work)
{}

/* called with mc_lock */
static void mld_ifc_event(struct inet6_dev *idev)
{}

static void mld_mca_work(struct work_struct *work)
{}

/* Device changing type */

void ipv6_mc_unmap(struct inet6_dev *idev)
{}

void ipv6_mc_remap(struct inet6_dev *idev)
{}

/* Device going down */
void ipv6_mc_down(struct inet6_dev *idev)
{}

static void ipv6_mc_reset(struct inet6_dev *idev)
{}

/* Device going up */

void ipv6_mc_up(struct inet6_dev *idev)
{}

/* IPv6 device initialization. */

void ipv6_mc_init_dev(struct inet6_dev *idev)
{}

/*
 *	Device is about to be destroyed: clean up.
 */

void ipv6_mc_destroy_dev(struct inet6_dev *idev)
{}

static void ipv6_mc_rejoin_groups(struct inet6_dev *idev)
{}

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

static struct notifier_block igmp6_netdev_notifier =;

#ifdef CONFIG_PROC_FS
struct igmp6_mc_iter_state {};

#define igmp6_mc_seq_private(seq)

static inline struct ifmcaddr6 *igmp6_mc_get_first(struct seq_file *seq)
{}

static struct ifmcaddr6 *igmp6_mc_get_next(struct seq_file *seq, struct ifmcaddr6 *im)
{}

static struct ifmcaddr6 *igmp6_mc_get_idx(struct seq_file *seq, loff_t pos)
{}

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

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

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

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

static const struct seq_operations igmp6_mc_seq_ops =;

struct igmp6_mcf_iter_state {};

#define igmp6_mcf_seq_private(seq)

static inline struct ip6_sf_list *igmp6_mcf_get_first(struct seq_file *seq)
{}

static struct ip6_sf_list *igmp6_mcf_get_next(struct seq_file *seq, struct ip6_sf_list *psf)
{}

static struct ip6_sf_list *igmp6_mcf_get_idx(struct seq_file *seq, loff_t pos)
{}

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

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

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

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

static const struct seq_operations igmp6_mcf_seq_ops =;

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

static void __net_exit igmp6_proc_exit(struct net *net)
{}
#else
static inline int igmp6_proc_init(struct net *net)
{
	return 0;
}
static inline void igmp6_proc_exit(struct net *net)
{
}
#endif

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

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

static struct pernet_operations igmp6_net_ops =;

int __init igmp6_init(void)
{}

int __init igmp6_late_init(void)
{}

void igmp6_cleanup(void)
{}

void igmp6_late_cleanup(void)
{}