linux/net/ipv4/igmp.c

// SPDX-License-Identifier: GPL-2.0-or-later
/*
 *	Linux NET3:	Internet Group Management Protocol  [IGMP]
 *
 *	This code implements the IGMP protocol as defined in RFC1112. There has
 *	been a further revision of this protocol since which is now supported.
 *
 *	If you have trouble with this module be careful what gcc you have used,
 *	the older version didn't come out right using gcc 2.5.8, the newer one
 *	seems to fall out with gcc 2.6.2.
 *
 *	Authors:
 *		Alan Cox <[email protected]>
 *
 *	Fixes:
 *
 *		Alan Cox	:	Added lots of __inline__ to optimise
 *					the memory usage of all the tiny little
 *					functions.
 *		Alan Cox	:	Dumped the header building experiment.
 *		Alan Cox	:	Minor tweaks ready for multicast routing
 *					and extended IGMP protocol.
 *		Alan Cox	:	Removed a load of inline directives. Gcc 2.5.8
 *					writes utterly bogus code otherwise (sigh)
 *					fixed IGMP loopback to behave in the manner
 *					desired by mrouted, fixed the fact it has been
 *					broken since 1.3.6 and cleaned up a few minor
 *					points.
 *
 *		Chih-Jen Chang	:	Tried to revise IGMP to Version 2
 *		Tsu-Sheng Tsao		E-mail: [email protected] and [email protected]
 *					The enhancements are mainly based on Steve Deering's
 * 					ipmulti-3.5 source code.
 *		Chih-Jen Chang	:	Added the igmp_get_mrouter_info and
 *		Tsu-Sheng Tsao		igmp_set_mrouter_info to keep track of
 *					the mrouted version on that device.
 *		Chih-Jen Chang	:	Added the max_resp_time parameter to
 *		Tsu-Sheng Tsao		igmp_heard_query(). Using this parameter
 *					to identify the multicast router version
 *					and do what the IGMP version 2 specified.
 *		Chih-Jen Chang	:	Added a timer to revert to IGMP V2 router
 *		Tsu-Sheng Tsao		if the specified time expired.
 *		Alan Cox	:	Stop IGMP from 0.0.0.0 being accepted.
 *		Alan Cox	:	Use GFP_ATOMIC in the right places.
 *		Christian Daudt :	igmp timer wasn't set for local group
 *					memberships but was being deleted,
 *					which caused a "del_timer() called
 *					from %p with timer not initialized\n"
 *					message (960131).
 *		Christian Daudt :	removed del_timer from
 *					igmp_timer_expire function (960205).
 *             Christian Daudt :       igmp_heard_report now only calls
 *                                     igmp_timer_expire if tm->running is
 *                                     true (960216).
 *		Malcolm Beattie :	ttl comparison wrong in igmp_rcv made
 *					igmp_heard_query never trigger. Expiry
 *					miscalculation fixed in igmp_heard_query
 *					and random() made to return unsigned to
 *					prevent negative expiry times.
 *		Alexey Kuznetsov:	Wrong group leaving behaviour, backport
 *					fix from pending 2.1.x patches.
 *		Alan Cox:		Forget to enable FDDI support earlier.
 *		Alexey Kuznetsov:	Fixed leaving groups on device down.
 *		Alexey Kuznetsov:	Accordance to igmp-v2-06 draft.
 *		David L Stevens:	IGMPv3 support, with help from
 *					Vinay Kulkarni
 */

#include <linux/module.h>
#include <linux/slab.h>
#include <linux/uaccess.h>
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/jiffies.h>
#include <linux/string.h>
#include <linux/socket.h>
#include <linux/sockios.h>
#include <linux/in.h>
#include <linux/inet.h>
#include <linux/netdevice.h>
#include <linux/skbuff.h>
#include <linux/inetdevice.h>
#include <linux/igmp.h>
#include <linux/if_arp.h>
#include <linux/rtnetlink.h>
#include <linux/times.h>
#include <linux/pkt_sched.h>
#include <linux/byteorder/generic.h>

#include <net/net_namespace.h>
#include <net/arp.h>
#include <net/ip.h>
#include <net/protocol.h>
#include <net/route.h>
#include <net/sock.h>
#include <net/checksum.h>
#include <net/inet_common.h>
#include <linux/netfilter_ipv4.h>
#ifdef CONFIG_IP_MROUTE
#include <linux/mroute.h>
#endif
#ifdef CONFIG_PROC_FS
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#endif

#ifdef CONFIG_IP_MULTICAST
/* Parameter names and values are taken from igmp-v2-06 draft */

#define IGMP_QUERY_INTERVAL
#define IGMP_QUERY_RESPONSE_INTERVAL

#define IGMP_INITIAL_REPORT_DELAY

/* IGMP_INITIAL_REPORT_DELAY is not from IGMP specs!
 * IGMP specs require to report membership immediately after
 * joining a group, but we delay the first report by a
 * small interval. It seems more natural and still does not
 * contradict to specs provided this delay is small enough.
 */

#define IGMP_V1_SEEN(in_dev)
#define IGMP_V2_SEEN(in_dev)

static int unsolicited_report_interval(struct in_device *in_dev)
{}

static void igmpv3_add_delrec(struct in_device *in_dev, struct ip_mc_list *im,
			      gfp_t gfp);
static void igmpv3_del_delrec(struct in_device *in_dev, struct ip_mc_list *im);
static void igmpv3_clear_delrec(struct in_device *in_dev);
static int sf_setstate(struct ip_mc_list *pmc);
static void sf_markstate(struct ip_mc_list *pmc);
#endif
static void ip_mc_clear_src(struct ip_mc_list *pmc);
static int ip_mc_add_src(struct in_device *in_dev, __be32 *pmca, int sfmode,
			 int sfcount, __be32 *psfsrc, int delta);

static void ip_ma_put(struct ip_mc_list *im)
{}

#define for_each_pmc_rcu(in_dev, pmc)

#define for_each_pmc_rtnl(in_dev, pmc)

static void ip_sf_list_clear_all(struct ip_sf_list *psf)
{}

#ifdef CONFIG_IP_MULTICAST

/*
 *	Timer management
 */

static void igmp_stop_timer(struct ip_mc_list *im)
{}

/* It must be called with locked im->lock */
static void igmp_start_timer(struct ip_mc_list *im, int max_delay)
{}

static void igmp_gq_start_timer(struct in_device *in_dev)
{}

static void igmp_ifc_start_timer(struct in_device *in_dev, int delay)
{}

static void igmp_mod_timer(struct ip_mc_list *im, int max_delay)
{}


/*
 *	Send an IGMP report.
 */

#define IGMP_SIZE


static int is_in(struct ip_mc_list *pmc, struct ip_sf_list *psf, int type,
	int gdeleted, int sdeleted)
{}

static int
igmp_scount(struct ip_mc_list *pmc, int type, int gdeleted, int sdeleted)
{}

/* source address selection per RFC 3376 section 4.2.13 */
static __be32 igmpv3_get_srcaddr(struct net_device *dev,
				 const struct flowi4 *fl4)
{}

static struct sk_buff *igmpv3_newpack(struct net_device *dev, unsigned int mtu)
{}

static int igmpv3_sendpack(struct sk_buff *skb)
{}

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

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

#define AVAILABLE(skb)

static struct sk_buff *add_grec(struct sk_buff *skb, struct ip_mc_list *pmc,
	int type, int gdeleted, int sdeleted)
{}

static int igmpv3_send_report(struct in_device *in_dev, struct ip_mc_list *pmc)
{}

/*
 * remove zero-count source records from a source filter list
 */
static void igmpv3_clear_zeros(struct ip_sf_list **ppsf)
{}

static void kfree_pmc(struct ip_mc_list *pmc)
{}

static void igmpv3_send_cr(struct in_device *in_dev)
{}

static int igmp_send_report(struct in_device *in_dev, struct ip_mc_list *pmc,
	int type)
{}

static void igmp_gq_timer_expire(struct timer_list *t)
{}

static void igmp_ifc_timer_expire(struct timer_list *t)
{}

static void igmp_ifc_event(struct in_device *in_dev)
{}


static void igmp_timer_expire(struct timer_list *t)
{}

/* mark EXCLUDE-mode sources */
static int igmp_xmarksources(struct ip_mc_list *pmc, int nsrcs, __be32 *srcs)
{}

static int igmp_marksources(struct ip_mc_list *pmc, int nsrcs, __be32 *srcs)
{}

/* return true if packet was dropped */
static bool igmp_heard_report(struct in_device *in_dev, __be32 group)
{}

/* return true if packet was dropped */
static bool igmp_heard_query(struct in_device *in_dev, struct sk_buff *skb,
	int len)
{}

/* called in rcu_read_lock() section */
int igmp_rcv(struct sk_buff *skb)
{}

#endif


/*
 *	Add a filter to a device
 */

static void ip_mc_filter_add(struct in_device *in_dev, __be32 addr)
{}

/*
 *	Remove a filter from a device
 */

static void ip_mc_filter_del(struct in_device *in_dev, __be32 addr)
{}

#ifdef CONFIG_IP_MULTICAST
/*
 * deleted ip_mc_list manipulation
 */
static void igmpv3_add_delrec(struct in_device *in_dev, struct ip_mc_list *im,
			      gfp_t gfp)
{}

/*
 * restore ip_mc_list deleted records
 */
static void igmpv3_del_delrec(struct in_device *in_dev, struct ip_mc_list *im)
{}

/*
 * flush ip_mc_list deleted records
 */
static void igmpv3_clear_delrec(struct in_device *in_dev)
{}
#endif

static void __igmp_group_dropped(struct ip_mc_list *im, gfp_t gfp)
{}

static void igmp_group_dropped(struct ip_mc_list *im)
{}

static void igmp_group_added(struct ip_mc_list *im)
{}


/*
 *	Multicast list managers
 */

static u32 ip_mc_hash(const struct ip_mc_list *im)
{}

static void ip_mc_hash_add(struct in_device *in_dev,
			   struct ip_mc_list *im)
{}

static void ip_mc_hash_remove(struct in_device *in_dev,
			      struct ip_mc_list *im)
{}


/*
 *	A socket has joined a multicast group on device dev.
 */
static void ____ip_mc_inc_group(struct in_device *in_dev, __be32 addr,
				unsigned int mode, gfp_t gfp)
{}

void __ip_mc_inc_group(struct in_device *in_dev, __be32 addr, gfp_t gfp)
{}
EXPORT_SYMBOL();

void ip_mc_inc_group(struct in_device *in_dev, __be32 addr)
{}
EXPORT_SYMBOL();

static int ip_mc_check_iphdr(struct sk_buff *skb)
{}

static int ip_mc_check_igmp_reportv3(struct sk_buff *skb)
{}

static int ip_mc_check_igmp_query(struct sk_buff *skb)
{}

static int ip_mc_check_igmp_msg(struct sk_buff *skb)
{}

static __sum16 ip_mc_validate_checksum(struct sk_buff *skb)
{}

static int ip_mc_check_igmp_csum(struct sk_buff *skb)
{}

/**
 * ip_mc_check_igmp - checks whether this is a sane IGMP packet
 * @skb: the skb to validate
 *
 * Checks whether an IPv4 packet is a valid IGMP packet. If so sets
 * skb transport header accordingly and returns zero.
 *
 * -EINVAL: A broken packet was detected, i.e. it violates some internet
 *  standard
 * -ENOMSG: IP header validation succeeded but it is not an IGMP packet.
 * -ENOMEM: A memory allocation failure happened.
 *
 * Caller needs to set the skb network header and free any returned skb if it
 * differs from the provided skb.
 */
int ip_mc_check_igmp(struct sk_buff *skb)
{}
EXPORT_SYMBOL();

/*
 *	Resend IGMP JOIN report; used by netdev notifier.
 */
static void ip_mc_rejoin_groups(struct in_device *in_dev)
{}

/*
 *	A socket has left a multicast group on device dev
 */

void __ip_mc_dec_group(struct in_device *in_dev, __be32 addr, gfp_t gfp)
{}
EXPORT_SYMBOL();

/* Device changing type */

void ip_mc_unmap(struct in_device *in_dev)
{}

void ip_mc_remap(struct in_device *in_dev)
{}

/* Device going down */

void ip_mc_down(struct in_device *in_dev)
{}

#ifdef CONFIG_IP_MULTICAST
static void ip_mc_reset(struct in_device *in_dev)
{}
#else
static void ip_mc_reset(struct in_device *in_dev)
{
}
#endif

void ip_mc_init_dev(struct in_device *in_dev)
{}

/* Device going up */

void ip_mc_up(struct in_device *in_dev)
{}

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

void ip_mc_destroy_dev(struct in_device *in_dev)
{}

/* RTNL is locked */
static struct in_device *ip_mc_find_dev(struct net *net, struct ip_mreqn *imr)
{}

/*
 *	Join a socket to a group
 */

static int ip_mc_del1_src(struct ip_mc_list *pmc, int sfmode,
	__be32 *psfsrc)
{}

#ifndef CONFIG_IP_MULTICAST
#define igmp_ifc_event
#endif

static int ip_mc_del_src(struct in_device *in_dev, __be32 *pmca, int sfmode,
			 int sfcount, __be32 *psfsrc, int delta)
{}

/*
 * Add multicast single-source filter to the interface list
 */
static int ip_mc_add1_src(struct ip_mc_list *pmc, int sfmode,
	__be32 *psfsrc)
{}

#ifdef CONFIG_IP_MULTICAST
static void sf_markstate(struct ip_mc_list *pmc)
{}

static int sf_setstate(struct ip_mc_list *pmc)
{}
#endif

/*
 * Add multicast source filter list to the interface list
 */
static int ip_mc_add_src(struct in_device *in_dev, __be32 *pmca, int sfmode,
			 int sfcount, __be32 *psfsrc, int delta)
{}

static void ip_mc_clear_src(struct ip_mc_list *pmc)
{}

/* Join a multicast group
 */
static int __ip_mc_join_group(struct sock *sk, struct ip_mreqn *imr,
			      unsigned int mode)
{}

/* Join ASM (Any-Source Multicast) group
 */
int ip_mc_join_group(struct sock *sk, struct ip_mreqn *imr)
{}
EXPORT_SYMBOL();

/* Join SSM (Source-Specific Multicast) group
 */
int ip_mc_join_group_ssm(struct sock *sk, struct ip_mreqn *imr,
			 unsigned int mode)
{}

static int ip_mc_leave_src(struct sock *sk, struct ip_mc_socklist *iml,
			   struct in_device *in_dev)
{}

int ip_mc_leave_group(struct sock *sk, struct ip_mreqn *imr)
{}
EXPORT_SYMBOL();

int ip_mc_source(int add, int omode, struct sock *sk, struct
	ip_mreq_source *mreqs, int ifindex)
{}

int ip_mc_msfilter(struct sock *sk, struct ip_msfilter *msf, int ifindex)
{}
int ip_mc_msfget(struct sock *sk, struct ip_msfilter *msf,
		 sockptr_t optval, sockptr_t optlen)
{}

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

/*
 * check if a multicast source filter allows delivery for a given <src,dst,intf>
 */
int ip_mc_sf_allow(const struct sock *sk, __be32 loc_addr, __be32 rmt_addr,
		   int dif, int sdif)
{}

/*
 *	A socket is closing.
 */

void ip_mc_drop_socket(struct sock *sk)
{}

/* called with rcu_read_lock() */
int ip_check_mc_rcu(struct in_device *in_dev, __be32 mc_addr, __be32 src_addr, u8 proto)
{}

#if defined(CONFIG_PROC_FS)
struct igmp_mc_iter_state {};

#define igmp_mc_seq_private(seq)

static inline struct ip_mc_list *igmp_mc_get_first(struct seq_file *seq)
{}

static struct ip_mc_list *igmp_mc_get_next(struct seq_file *seq, struct ip_mc_list *im)
{}

static struct ip_mc_list *igmp_mc_get_idx(struct seq_file *seq, loff_t pos)
{}

static void *igmp_mc_seq_start(struct seq_file *seq, loff_t *pos)
	__acquires(rcu)
{}

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

static void igmp_mc_seq_stop(struct seq_file *seq, void *v)
	__releases(rcu)
{}

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

static const struct seq_operations igmp_mc_seq_ops =;

struct igmp_mcf_iter_state {};

#define igmp_mcf_seq_private(seq)

static inline struct ip_sf_list *igmp_mcf_get_first(struct seq_file *seq)
{}

static struct ip_sf_list *igmp_mcf_get_next(struct seq_file *seq, struct ip_sf_list *psf)
{}

static struct ip_sf_list *igmp_mcf_get_idx(struct seq_file *seq, loff_t pos)
{}

static void *igmp_mcf_seq_start(struct seq_file *seq, loff_t *pos)
	__acquires(rcu)
{}

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

static void igmp_mcf_seq_stop(struct seq_file *seq, void *v)
	__releases(rcu)
{}

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

static const struct seq_operations igmp_mcf_seq_ops =;

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

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

static struct pernet_operations igmp_net_ops =;
#endif

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

static struct notifier_block igmp_notifier =;

int __init igmp_mc_init(void)
{}