Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
L
linux
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
Kirill Smelkov
linux
Commits
06da9228
Commit
06da9228
authored
Apr 15, 2003
by
David Stevens
Committed by
David S. Miller
Apr 15, 2003
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
[IPV6]: Add MLDv2 support.
parent
98fab1e4
Changes
14
Hide whitespace changes
Inline
Side-by-side
Showing
14 changed files
with
1708 additions
and
99 deletions
+1708
-99
include/linux/icmpv6.h
include/linux/icmpv6.h
+13
-0
include/linux/in.h
include/linux/in.h
+33
-0
include/linux/ip.h
include/linux/ip.h
+1
-0
include/linux/tcp.h
include/linux/tcp.h
+1
-0
include/linux/udp.h
include/linux/udp.h
+1
-0
include/net/addrconf.h
include/net/addrconf.h
+28
-39
include/net/icmp.h
include/net/icmp.h
+1
-0
include/net/if_inet6.h
include/net/if_inet6.h
+42
-0
net/ipv6/ip6_input.c
net/ipv6/ip6_input.c
+1
-1
net/ipv6/ip6_output.c
net/ipv6/ip6_output.c
+2
-1
net/ipv6/ipv6_sockglue.c
net/ipv6/ipv6_sockglue.c
+102
-0
net/ipv6/mcast.c
net/ipv6/mcast.c
+1481
-56
net/ipv6/raw.c
net/ipv6/raw.c
+1
-1
net/ipv6/udp.c
net/ipv6/udp.c
+1
-1
No files found.
include/linux/icmpv6.h
View file @
06da9228
...
...
@@ -86,6 +86,19 @@ struct icmp6hdr {
#define ICMPV6_MGM_REPORT 131
#define ICMPV6_MGM_REDUCTION 132
/* definitions for MLDv2 */
#define MLD2_MODE_IS_INCLUDE 1
#define MLD2_MODE_IS_EXCLUDE 2
#define MLD2_CHANGE_TO_INCLUDE 3
#define MLD2_CHANGE_TO_EXCLUDE 4
#define MLD2_ALLOW_NEW_SOURCES 5
#define MLD2_BLOCK_OLD_SOURCES 6
/* this must be an IANA-assigned value; 206 for testing only */
#define ICMPV6_MLD2_REPORT 206
#define MLD2_ALL_MCR_INIT { { { 0xff,0x02,0,0,0,0,0,0,0,0,0,0,0,0,0,0x16 } } }
/*
* Codes for Destination Unreachable
*/
...
...
include/linux/in.h
View file @
06da9228
...
...
@@ -90,6 +90,13 @@ struct in_addr {
#define IP_ADD_SOURCE_MEMBERSHIP 39
#define IP_DROP_SOURCE_MEMBERSHIP 40
#define IP_MSFILTER 41
#define MCAST_JOIN_GROUP 42
#define MCAST_BLOCK_SOURCE 43
#define MCAST_UNBLOCK_SOURCE 44
#define MCAST_LEAVE_GROUP 45
#define MCAST_JOIN_SOURCE_GROUP 46
#define MCAST_LEAVE_SOURCE_GROUP 47
#define MCAST_MSFILTER 48
#define MCAST_EXCLUDE 0
#define MCAST_INCLUDE 1
...
...
@@ -131,6 +138,32 @@ struct ip_msfilter {
(sizeof(struct ip_msfilter) - sizeof(__u32) \
+ (numsrc) * sizeof(__u32))
struct
group_req
{
__u32
gr_interface
;
/* interface index */
struct
sockaddr_storage
gr_group
;
/* group address */
};
struct
group_source_req
{
__u32
gsr_interface
;
/* interface index */
struct
sockaddr_storage
gsr_group
;
/* group address */
struct
sockaddr_storage
gsr_source
;
/* source address */
};
struct
group_filter
{
__u32
gf_interface
;
/* interface index */
struct
sockaddr_storage
gf_group
;
/* multicast address */
__u32
gf_fmode
;
/* filter mode */
__u32
gf_numsrc
;
/* number of sources */
struct
sockaddr_storage
gf_slist
[
1
];
/* interface index */
};
#define GROUP_FILTER_SIZE(numsrc) \
(sizeof(struct group_filter) - sizeof(struct sockaddr_storage) \
+ (numsrc) * sizeof(struct sockaddr_storage))
struct
in_pktinfo
{
int
ipi_ifindex
;
...
...
include/linux/ip.h
View file @
06da9228
...
...
@@ -79,6 +79,7 @@
#define IPOPT_TS_PRESPEC 3
/* specified modules only */
#ifdef __KERNEL__
#include <linux/config.h>
#include <linux/types.h>
#include <net/sock.h>
#include <linux/igmp.h>
...
...
include/linux/tcp.h
View file @
06da9228
...
...
@@ -17,6 +17,7 @@
#ifndef _LINUX_TCP_H
#define _LINUX_TCP_H
#include <linux/config.h>
#include <linux/skbuff.h>
#include <asm/byteorder.h>
#include <net/sock.h>
...
...
include/linux/udp.h
View file @
06da9228
...
...
@@ -17,6 +17,7 @@
#ifndef _LINUX_UDP_H
#define _LINUX_UDP_H
#include <linux/config.h>
#include <asm/byteorder.h>
#include <net/sock.h>
#include <linux/ip.h>
...
...
include/net/addrconf.h
View file @
06da9228
...
...
@@ -76,49 +76,38 @@ extern void addrconf_leave_solict(struct net_device *dev,
/*
* multicast prototypes (mcast.c)
*/
extern
int
ipv6_sock_mc_join
(
struct
sock
*
sk
,
int
ifindex
,
struct
in6_addr
*
addr
);
extern
int
ipv6_sock_mc_drop
(
struct
sock
*
sk
,
int
ifindex
,
struct
in6_addr
*
addr
);
extern
void
ipv6_sock_mc_close
(
struct
sock
*
sk
);
extern
int
inet6_mc_check
(
struct
sock
*
sk
,
struct
in6_addr
*
addr
);
extern
int
ipv6_dev_mc_inc
(
struct
net_device
*
dev
,
struct
in6_addr
*
addr
);
extern
int
ipv6_dev_mc_dec
(
struct
net_device
*
dev
,
struct
in6_addr
*
addr
);
extern
void
ipv6_mc_up
(
struct
inet6_dev
*
idev
);
extern
void
ipv6_mc_down
(
struct
inet6_dev
*
idev
);
extern
void
ipv6_mc_init_dev
(
struct
inet6_dev
*
idev
);
extern
void
ipv6_mc_destroy_dev
(
struct
inet6_dev
*
idev
);
extern
void
addrconf_dad_failure
(
struct
inet6_ifaddr
*
ifp
);
extern
int
ipv6_chk_mcast_addr
(
struct
net_device
*
dev
,
struct
in6_addr
*
addr
);
extern
void
addrconf_prefix_rcv
(
struct
net_device
*
dev
,
u8
*
opt
,
int
len
);
extern
int
ipv6_sock_mc_join
(
struct
sock
*
sk
,
int
ifindex
,
struct
in6_addr
*
addr
);
extern
int
ipv6_sock_mc_drop
(
struct
sock
*
sk
,
int
ifindex
,
struct
in6_addr
*
addr
);
extern
void
ipv6_sock_mc_close
(
struct
sock
*
sk
);
extern
int
inet6_mc_check
(
struct
sock
*
sk
,
struct
in6_addr
*
mc_addr
,
struct
in6_addr
*
src_addr
);
extern
int
ipv6_dev_mc_inc
(
struct
net_device
*
dev
,
struct
in6_addr
*
addr
);
extern
int
ipv6_dev_mc_dec
(
struct
net_device
*
dev
,
struct
in6_addr
*
addr
);
extern
void
ipv6_mc_up
(
struct
inet6_dev
*
idev
);
extern
void
ipv6_mc_down
(
struct
inet6_dev
*
idev
);
extern
void
ipv6_mc_init_dev
(
struct
inet6_dev
*
idev
);
extern
void
ipv6_mc_destroy_dev
(
struct
inet6_dev
*
idev
);
extern
void
addrconf_dad_failure
(
struct
inet6_ifaddr
*
ifp
);
extern
int
ipv6_chk_mcast_addr
(
struct
net_device
*
dev
,
struct
in6_addr
*
group
,
struct
in6_addr
*
src_addr
);
extern
void
addrconf_prefix_rcv
(
struct
net_device
*
dev
,
u8
*
opt
,
int
len
);
/*
* anycast prototypes (anycast.c)
*/
extern
int
ipv6_sock_ac_join
(
struct
sock
*
sk
,
int
ifindex
,
struct
in6_addr
*
addr
);
extern
int
ipv6_sock_ac_drop
(
struct
sock
*
sk
,
int
ifindex
,
struct
in6_addr
*
addr
);
extern
void
ipv6_sock_ac_close
(
struct
sock
*
sk
);
extern
int
inet6_ac_check
(
struct
sock
*
sk
,
struct
in6_addr
*
addr
,
int
ifindex
);
extern
int
ipv6_dev_ac_inc
(
struct
net_device
*
dev
,
struct
in6_addr
*
addr
);
extern
int
ipv6_dev_ac_dec
(
struct
net_device
*
dev
,
struct
in6_addr
*
addr
);
extern
int
ipv6_chk_acast_addr
(
struct
net_device
*
dev
,
struct
in6_addr
*
addr
);
extern
int
ipv6_sock_ac_join
(
struct
sock
*
sk
,
int
ifindex
,
struct
in6_addr
*
addr
);
extern
int
ipv6_sock_ac_drop
(
struct
sock
*
sk
,
int
ifindex
,
struct
in6_addr
*
addr
);
extern
void
ipv6_sock_ac_close
(
struct
sock
*
sk
);
extern
int
inet6_ac_check
(
struct
sock
*
sk
,
struct
in6_addr
*
addr
,
int
ifindex
);
extern
int
ipv6_dev_ac_inc
(
struct
net_device
*
dev
,
struct
in6_addr
*
addr
);
extern
int
ipv6_dev_ac_dec
(
struct
net_device
*
dev
,
struct
in6_addr
*
addr
);
extern
int
ipv6_chk_acast_addr
(
struct
net_device
*
dev
,
struct
in6_addr
*
addr
);
/* Device notifier */
...
...
include/net/icmp.h
View file @
06da9228
...
...
@@ -18,6 +18,7 @@
#ifndef _ICMP_H
#define _ICMP_H
#include <linux/config.h>
#include <linux/icmp.h>
#include <linux/skbuff.h>
...
...
include/net/if_inet6.h
View file @
06da9228
...
...
@@ -52,27 +52,58 @@ struct inet6_ifaddr
int
dead
;
};
struct
ip6_sf_socklist
{
unsigned
int
sl_max
;
unsigned
int
sl_count
;
struct
in6_addr
sl_addr
[
0
];
};
#define IP6_SFLSIZE(count) (sizeof(struct ip6_sf_socklist) + \
(count) * sizeof(struct in6_addr))
#define IP6_SFBLOCK 10
/* allocate this many at once */
struct
ipv6_mc_socklist
{
struct
in6_addr
addr
;
int
ifindex
;
struct
ipv6_mc_socklist
*
next
;
unsigned
int
sfmode
;
/* MCAST_{INCLUDE,EXCLUDE} */
struct
ip6_sf_socklist
*
sflist
;
};
struct
ip6_sf_list
{
struct
ip6_sf_list
*
sf_next
;
struct
in6_addr
sf_addr
;
unsigned
long
sf_count
[
2
];
/* include/exclude counts */
unsigned
char
sf_gsresp
;
/* include in g & s response? */
unsigned
char
sf_oldin
;
/* change state */
unsigned
char
sf_crcount
;
/* retrans. left to send */
};
#define MAF_TIMER_RUNNING 0x01
#define MAF_LAST_REPORTER 0x02
#define MAF_LOADED 0x04
#define MAF_NOREPORT 0x08
#define MAF_GSQUERY 0x10
struct
ifmcaddr6
{
struct
in6_addr
mca_addr
;
struct
inet6_dev
*
idev
;
struct
ifmcaddr6
*
next
;
struct
ip6_sf_list
*
mca_sources
;
struct
ip6_sf_list
*
mca_tomb
;
unsigned
int
mca_sfmode
;
unsigned
long
mca_sfcount
[
2
];
struct
timer_list
mca_timer
;
unsigned
mca_flags
;
int
mca_users
;
atomic_t
mca_refcnt
;
spinlock_t
mca_lock
;
unsigned
char
mca_crcount
;
};
/* Anycast stuff */
...
...
@@ -126,7 +157,18 @@ struct inet6_dev
struct
net_device
*
dev
;
struct
inet6_ifaddr
*
addr_list
;
struct
ifmcaddr6
*
mc_list
;
struct
ifmcaddr6
*
mc_tomb
;
rwlock_t
mc_lock
;
unsigned
long
mc_v1_seen
;
unsigned
long
mc_maxdelay
;
unsigned
char
mc_qrv
;
unsigned
char
mc_gq_running
;
unsigned
char
mc_ifc_count
;
struct
timer_list
mc_gq_timer
;
/* general query timer */
struct
timer_list
mc_ifc_timer
;
/* interface change timer */
struct
ifacaddr6
*
ac_list
;
rwlock_t
lock
;
atomic_t
refcnt
;
...
...
net/ipv6/ip6_input.c
View file @
06da9228
...
...
@@ -217,7 +217,7 @@ int ip6_mc_input(struct sk_buff *skb)
IP6_INC_STATS_BH
(
Ip6InMcastPkts
);
hdr
=
skb
->
nh
.
ipv6h
;
if
(
ipv6_chk_mcast_addr
(
skb
->
dev
,
&
hdr
->
daddr
))
if
(
ipv6_chk_mcast_addr
(
skb
->
dev
,
&
hdr
->
daddr
,
&
hdr
->
saddr
))
deliver
=
1
;
/*
...
...
net/ipv6/ip6_output.c
View file @
06da9228
...
...
@@ -110,7 +110,8 @@ int ip6_output(struct sk_buff *skb)
struct
ipv6_pinfo
*
np
=
skb
->
sk
?
inet6_sk
(
skb
->
sk
)
:
NULL
;
if
(
!
(
dev
->
flags
&
IFF_LOOPBACK
)
&&
(
!
np
||
np
->
mc_loop
)
&&
ipv6_chk_mcast_addr
(
dev
,
&
skb
->
nh
.
ipv6h
->
daddr
))
{
ipv6_chk_mcast_addr
(
dev
,
&
skb
->
nh
.
ipv6h
->
daddr
,
&
skb
->
nh
.
ipv6h
->
saddr
))
{
struct
sk_buff
*
newskb
=
skb_clone
(
skb
,
GFP_ATOMIC
);
/* Do not check for IFF_ALLMULTI; multicast routing
...
...
net/ipv6/ipv6_sockglue.c
View file @
06da9228
...
...
@@ -19,6 +19,10 @@
* o Return -EINVAL for setsockopt of short lengths
* o Truncate getsockopt returns
* o Return an optlen of the truncated length if need be
*
* Changes:
* David L Stevens <dlstevens@us.ibm.com>:
* - added multicast source filtering API for MLDv2
*/
#include <linux/module.h>
...
...
@@ -375,6 +379,89 @@ int ipv6_setsockopt(struct sock *sk, int level, int optname, char *optval,
retv
=
ipv6_sock_ac_drop
(
sk
,
mreq
.
ipv6mr_ifindex
,
&
mreq
.
ipv6mr_acaddr
);
break
;
}
case
MCAST_JOIN_GROUP
:
case
MCAST_LEAVE_GROUP
:
{
struct
group_req
greq
;
struct
sockaddr_in6
*
psin6
;
retv
=
-
EFAULT
;
if
(
copy_from_user
(
&
greq
,
optval
,
sizeof
(
struct
group_req
)))
break
;
if
(
greq
.
gr_group
.
ss_family
!=
AF_INET6
)
{
retv
=
-
EADDRNOTAVAIL
;
break
;
}
psin6
=
(
struct
sockaddr_in6
*
)
&
greq
.
gr_group
;
if
(
optname
==
IPV6_ADD_MEMBERSHIP
)
retv
=
ipv6_sock_mc_join
(
sk
,
greq
.
gr_interface
,
&
psin6
->
sin6_addr
);
else
retv
=
ipv6_sock_mc_drop
(
sk
,
greq
.
gr_interface
,
&
psin6
->
sin6_addr
);
}
case
MCAST_JOIN_SOURCE_GROUP
:
case
MCAST_LEAVE_SOURCE_GROUP
:
case
MCAST_BLOCK_SOURCE
:
case
MCAST_UNBLOCK_SOURCE
:
{
struct
group_source_req
greqs
;
int
omode
,
add
;
if
(
optlen
!=
sizeof
(
struct
group_source_req
))
goto
e_inval
;
if
(
copy_from_user
(
&
greqs
,
optval
,
sizeof
(
greqs
)))
{
retv
=
-
EFAULT
;
break
;
}
if
(
greqs
.
gsr_group
.
ss_family
!=
AF_INET6
)
{
retv
=
-
EADDRNOTAVAIL
;
break
;
}
if
(
optname
==
MCAST_BLOCK_SOURCE
)
{
omode
=
MCAST_EXCLUDE
;
add
=
1
;
}
else
if
(
optname
==
MCAST_UNBLOCK_SOURCE
)
{
omode
=
MCAST_EXCLUDE
;
add
=
0
;
}
else
if
(
optname
==
MCAST_JOIN_SOURCE_GROUP
)
{
struct
sockaddr_in6
*
psin6
;
psin6
=
(
struct
sockaddr_in6
*
)
&
greqs
.
gsr_group
;
retv
=
ipv6_sock_mc_join
(
sk
,
greqs
.
gsr_interface
,
&
psin6
->
sin6_addr
);
if
(
retv
)
break
;
omode
=
MCAST_INCLUDE
;
add
=
1
;
}
else
/*IP_DROP_SOURCE_MEMBERSHIP */
{
omode
=
MCAST_INCLUDE
;
add
=
0
;
}
retv
=
ip6_mc_source
(
add
,
omode
,
sk
,
&
greqs
);
break
;
}
case
MCAST_MSFILTER
:
{
struct
group_filter
*
gsf
;
if
(
optlen
<
GROUP_FILTER_SIZE
(
0
))
goto
e_inval
;
gsf
=
(
struct
group_filter
*
)
kmalloc
(
optlen
,
GFP_KERNEL
);
if
(
gsf
==
0
)
{
retv
=
-
ENOBUFS
;
break
;
}
retv
=
-
EFAULT
;
if
(
copy_from_user
(
gsf
,
optval
,
optlen
))
{
kfree
(
gsf
);
break
;
}
retv
=
ip6_mc_msfilter
(
sk
,
gsf
);
kfree
(
gsf
);
break
;
}
case
IPV6_ROUTER_ALERT
:
retv
=
ip6_ra_control
(
sk
,
val
,
NULL
);
break
;
...
...
@@ -448,6 +535,21 @@ int ipv6_getsockopt(struct sock *sk, int level, int optname, char *optval,
return
-
ENOTCONN
;
val
=
sk
->
family
;
break
;
case
MCAST_MSFILTER
:
{
struct
group_filter
gsf
;
int
err
;
if
(
len
<
GROUP_FILTER_SIZE
(
0
))
return
-
EINVAL
;
if
(
copy_from_user
(
&
gsf
,
optval
,
GROUP_FILTER_SIZE
(
0
)))
return
-
EFAULT
;
lock_sock
(
sk
);
err
=
ip6_mc_msfget
(
sk
,
&
gsf
,
(
struct
group_filter
*
)
optval
,
optlen
);
release_sock
(
sk
);
return
err
;
}
case
IPV6_PKTOPTIONS
:
{
...
...
net/ipv6/mcast.c
View file @
06da9228
...
...
@@ -24,6 +24,8 @@
* YOSHIFUJI Hideaki @USAGI:
* - Ignore Queries for invalid addresses.
* - MLD for link-local addresses.
* David L Stevens <dlstevens@us.ibm.com>:
* - MLDv2 support
*/
#include <linux/config.h>
...
...
@@ -35,6 +37,7 @@
#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>
...
...
@@ -63,6 +66,58 @@
#define MDBG(x)
#endif
/*
* These header formats should be in a separate include file, but icmpv6.h
* doesn't have in6_addr defined in all cases, there is no __u128, and no
* other files reference these.
*
* +-DLS 4/14/03
*/
/* Multicast Listener Discovery version 2 headers */
struct
mld2_grec
{
__u8
grec_type
;
__u8
grec_auxwords
;
__u16
grec_nsrcs
;
struct
in6_addr
grec_mca
;
struct
in6_addr
grec_src
[
0
];
};
struct
mld2_report
{
__u8
type
;
__u8
resv1
;
__u16
csum
;
__u16
resv2
;
__u16
ngrec
;
struct
mld2_grec
grec
[
0
];
};
struct
mld2_query
{
__u8
type
;
__u8
code
;
__u16
csum
;
__u16
mrc
;
__u16
resv1
;
struct
in6_addr
mca
;
#if defined(__LITTLE_ENDIAN_BITFIELD)
__u8
qrv
:
3
,
suppress:
1
,
resv2:
4
;
#elif defined(__BIG_ENDIAN_BITFIELD)
__u8
resv2
:
4
,
suppress:
1
,
qrv:
3
;
#else
#error "Please fix <asm/byteorder.h>"
#endif
__u8
qqic
;
__u16
nsrcs
;
struct
in6_addr
srcs
[
0
];
};
struct
in6_addr
mld2_all_mcr
=
MLD2_ALL_MCR_INIT
;
/* Big mc list lock for all the sockets */
static
rwlock_t
ipv6_sk_mc_lock
=
RW_LOCK_UNLOCKED
;
...
...
@@ -72,7 +127,37 @@ static void igmp6_join_group(struct ifmcaddr6 *ma);
static
void
igmp6_leave_group
(
struct
ifmcaddr6
*
ma
);
static
void
igmp6_timer_handler
(
unsigned
long
data
);
static
void
mld_gq_timer_expire
(
unsigned
long
data
);
static
void
mld_ifc_timer_expire
(
unsigned
long
data
);
static
void
mld_ifc_event
(
struct
inet6_dev
*
idev
);
static
void
mld_add_delrec
(
struct
inet6_dev
*
idev
,
struct
ifmcaddr6
*
pmc
);
static
void
mld_del_delrec
(
struct
inet6_dev
*
idev
,
struct
in6_addr
*
addr
);
static
void
mld_clear_delrec
(
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
);
int
ip6_mc_del_src
(
struct
inet6_dev
*
idev
,
struct
in6_addr
*
pmca
,
int
sfmode
,
int
sfcount
,
struct
in6_addr
*
psfsrc
,
int
delta
);
int
ip6_mc_add_src
(
struct
inet6_dev
*
idev
,
struct
in6_addr
*
pmca
,
int
sfmode
,
int
sfcount
,
struct
in6_addr
*
psfsrc
,
int
delta
);
int
ip6_mc_leave_src
(
struct
sock
*
sk
,
struct
ipv6_mc_socklist
*
iml
,
struct
inet6_dev
*
idev
);
#define IGMP6_UNSOLICITED_IVAL (10*HZ)
#define MLD_QRV_DEFAULT 2
#define MLD_V1_SEEN(idev) ((idev)->mc_v1_seen && \
time_before(jiffies, (idev)->mc_v1_seen))
#define MLDV2_MASK(value, nb) ((nb)>=32 ? (value) : ((1<<(nb))-1) & (value))
#define MLDV2_EXP(thresh, nbmant, nbexp, value) \
((value) < (thresh) ? (value) : \
((MLDV2_MASK(value, nbmant) | (1<<(nbmant+nbexp))) << \
(MLDV2_MASK((value) >> (nbmant), nbexp) + (nbexp))))
#define MLDV2_QQIC(value) MLDV2_EXP(0x80, 4, 3, value)
#define MLDV2_MRC(value) MLDV2_EXP(0x8000, 12, 3, value)
/*
* socket join on multicast group
...
...
@@ -113,6 +198,8 @@ int ipv6_sock_mc_join(struct sock *sk, int ifindex, struct in6_addr *addr)
}
mc_lst
->
ifindex
=
dev
->
ifindex
;
mc_lst
->
sfmode
=
MCAST_EXCLUDE
;
mc_lst
->
sflist
=
0
;
/*
* now add/increase the group membership on the device
...
...
@@ -154,6 +241,12 @@ int ipv6_sock_mc_drop(struct sock *sk, int ifindex, struct in6_addr *addr)
write_unlock_bh
(
&
ipv6_sk_mc_lock
);
if
((
dev
=
dev_get_by_index
(
mc_lst
->
ifindex
))
!=
NULL
)
{
struct
inet6_dev
*
idev
=
in6_dev_get
(
dev
);
if
(
idev
)
{
(
void
)
ip6_mc_leave_src
(
sk
,
mc_lst
,
idev
);
in6_dev_put
(
idev
);
}
ipv6_dev_mc_dec
(
dev
,
&
mc_lst
->
addr
);
dev_put
(
dev
);
}
...
...
@@ -166,6 +259,40 @@ int ipv6_sock_mc_drop(struct sock *sk, int ifindex, struct in6_addr *addr)
return
-
ENOENT
;
}
struct
inet6_dev
*
ip6_mc_find_dev
(
struct
in6_addr
*
group
,
int
ifindex
)
{
struct
net_device
*
dev
=
0
;
struct
inet6_dev
*
idev
=
0
;
if
(
ifindex
==
0
)
{
struct
rt6_info
*
rt
;
rt
=
rt6_lookup
(
group
,
NULL
,
0
,
0
);
if
(
rt
)
{
dev
=
rt
->
rt6i_dev
;
dev_hold
(
dev
);
dst_release
(
&
rt
->
u
.
dst
);
}
}
else
dev
=
dev_get_by_index
(
ifindex
);
if
(
!
dev
)
return
0
;
idev
=
in6_dev_get
(
dev
);
if
(
!
idev
)
{
dev_put
(
dev
);
return
0
;
}
read_lock_bh
(
&
idev
->
lock
);
if
(
idev
->
dead
)
{
read_unlock_bh
(
&
idev
->
lock
);
in6_dev_put
(
idev
);
dev_put
(
dev
);
return
0
;
}
return
idev
;
}
void
ipv6_sock_mc_close
(
struct
sock
*
sk
)
{
struct
ipv6_pinfo
*
np
=
inet6_sk
(
sk
);
...
...
@@ -180,6 +307,12 @@ void ipv6_sock_mc_close(struct sock *sk)
dev
=
dev_get_by_index
(
mc_lst
->
ifindex
);
if
(
dev
)
{
struct
inet6_dev
*
idev
=
in6_dev_get
(
dev
);
if
(
idev
)
{
(
void
)
ip6_mc_leave_src
(
sk
,
mc_lst
,
idev
);
in6_dev_put
(
idev
);
}
ipv6_dev_mc_dec
(
dev
,
&
mc_lst
->
addr
);
dev_put
(
dev
);
}
...
...
@@ -191,21 +324,291 @@ void ipv6_sock_mc_close(struct sock *sk)
write_unlock_bh
(
&
ipv6_sk_mc_lock
);
}
int
inet6_mc_check
(
struct
sock
*
sk
,
struct
in6_addr
*
addr
)
int
ip6_mc_source
(
int
add
,
int
omode
,
struct
sock
*
sk
,
struct
group_source_req
*
pgsr
)
{
struct
in6_addr
*
source
,
*
group
;
struct
ipv6_mc_socklist
*
pmc
;
struct
net_device
*
dev
;
struct
inet6_dev
*
idev
;
struct
ipv6_pinfo
*
inet6
=
inet6_sk
(
sk
);
struct
ip6_sf_socklist
*
psl
;
int
i
,
j
,
rv
;
int
err
;
if
(
pgsr
->
gsr_group
.
ss_family
!=
AF_INET6
||
pgsr
->
gsr_source
.
ss_family
!=
AF_INET6
)
return
-
EINVAL
;
source
=
&
((
struct
sockaddr_in6
*
)
&
pgsr
->
gsr_source
)
->
sin6_addr
;
group
=
&
((
struct
sockaddr_in6
*
)
&
pgsr
->
gsr_group
)
->
sin6_addr
;
if
(
!
(
ipv6_addr_type
(
group
)
&
IPV6_ADDR_MULTICAST
))
return
-
EINVAL
;
idev
=
ip6_mc_find_dev
(
group
,
pgsr
->
gsr_interface
);
if
(
!
idev
)
return
-
ENODEV
;
dev
=
idev
->
dev
;
err
=
-
EADDRNOTAVAIL
;
for
(
pmc
=
inet6
->
ipv6_mc_list
;
pmc
;
pmc
=
pmc
->
next
)
{
if
(
pmc
->
ifindex
!=
pgsr
->
gsr_interface
)
continue
;
if
(
ipv6_addr_cmp
(
&
pmc
->
addr
,
group
)
==
0
)
break
;
}
if
(
!
pmc
)
/* must have a prior join */
goto
done
;
/* if a source filter was set, must be the same mode as before */
if
(
pmc
->
sflist
)
{
if
(
pmc
->
sfmode
!=
omode
)
goto
done
;
}
else
if
(
pmc
->
sfmode
!=
omode
)
{
/* allow mode switches for empty-set filters */
ip6_mc_del_src
(
idev
,
group
,
pmc
->
sfmode
,
0
,
0
,
0
);
pmc
->
sfmode
=
omode
;
ip6_mc_del_src
(
idev
,
group
,
pmc
->
sfmode
,
0
,
0
,
0
);
}
psl
=
pmc
->
sflist
;
if
(
!
add
)
{
if
(
!
psl
)
goto
done
;
rv
=
!
0
;
for
(
i
=
0
;
i
<
psl
->
sl_count
;
i
++
)
{
rv
=
memcmp
(
&
psl
->
sl_addr
,
group
,
sizeof
(
struct
in6_addr
));
if
(
rv
>=
0
)
break
;
}
if
(
!
rv
)
/* source not found */
goto
done
;
/* update the interface filter */
ip6_mc_del_src
(
idev
,
group
,
omode
,
1
,
source
,
1
);
for
(
j
=
i
+
1
;
j
<
psl
->
sl_count
;
j
++
)
psl
->
sl_addr
[
j
-
1
]
=
psl
->
sl_addr
[
j
];
psl
->
sl_count
--
;
err
=
0
;
goto
done
;
}
/* else, add a new source to the filter */
if
(
!
psl
||
psl
->
sl_count
==
psl
->
sl_max
)
{
struct
ip6_sf_socklist
*
newpsl
;
int
count
=
IP6_SFBLOCK
;
if
(
psl
)
count
+=
psl
->
sl_max
;
newpsl
=
(
struct
ip6_sf_socklist
*
)
sock_kmalloc
(
sk
,
IP6_SFLSIZE
(
count
),
GFP_ATOMIC
);
if
(
!
newpsl
)
{
err
=
-
ENOBUFS
;
goto
done
;
}
newpsl
->
sl_max
=
count
;
newpsl
->
sl_count
=
count
-
IP6_SFBLOCK
;
if
(
psl
)
{
for
(
i
=
0
;
i
<
psl
->
sl_count
;
i
++
)
newpsl
->
sl_addr
[
i
]
=
psl
->
sl_addr
[
i
];
sock_kfree_s
(
sk
,
psl
,
IP6_SFLSIZE
(
psl
->
sl_max
));
}
pmc
->
sflist
=
psl
=
newpsl
;
}
rv
=
1
;
/* > 0 for insert logic below if sl_count is 0 */
for
(
i
=
0
;
i
<
psl
->
sl_count
;
i
++
)
{
rv
=
memcmp
(
&
psl
->
sl_addr
,
group
,
sizeof
(
struct
in6_addr
));
if
(
rv
>=
0
)
break
;
}
if
(
rv
==
0
)
/* address already there is an error */
goto
done
;
for
(
j
=
psl
->
sl_count
-
1
;
j
>=
i
;
j
--
)
psl
->
sl_addr
[
j
+
1
]
=
psl
->
sl_addr
[
j
];
psl
->
sl_addr
[
i
]
=
*
source
;
psl
->
sl_count
++
;
err
=
0
;
/* update the interface list */
ip6_mc_add_src
(
idev
,
group
,
omode
,
1
,
source
,
1
);
done:
read_unlock_bh
(
&
idev
->
lock
);
in6_dev_put
(
idev
);
dev_put
(
dev
);
return
err
;
}
int
ip6_mc_msfilter
(
struct
sock
*
sk
,
struct
group_filter
*
gsf
)
{
struct
in6_addr
*
group
;
struct
ipv6_mc_socklist
*
pmc
;
struct
net_device
*
dev
;
struct
inet6_dev
*
idev
;
struct
ipv6_pinfo
*
inet6
=
inet6_sk
(
sk
);
struct
ip6_sf_socklist
*
newpsl
,
*
psl
;
int
i
,
err
;
group
=
&
((
struct
sockaddr_in6
*
)
&
gsf
->
gf_group
)
->
sin6_addr
;
if
(
!
(
ipv6_addr_type
(
group
)
&
IPV6_ADDR_MULTICAST
))
return
-
EINVAL
;
if
(
gsf
->
gf_fmode
!=
MCAST_INCLUDE
&&
gsf
->
gf_fmode
!=
MCAST_EXCLUDE
)
return
-
EINVAL
;
idev
=
ip6_mc_find_dev
(
group
,
gsf
->
gf_interface
);
if
(
!
idev
)
return
-
ENODEV
;
dev
=
idev
->
dev
;
err
=
-
EADDRNOTAVAIL
;
for
(
pmc
=
inet6
->
ipv6_mc_list
;
pmc
;
pmc
=
pmc
->
next
)
{
if
(
pmc
->
ifindex
!=
gsf
->
gf_interface
)
continue
;
if
(
ipv6_addr_cmp
(
&
pmc
->
addr
,
group
)
==
0
)
break
;
}
if
(
!
pmc
)
/* must have a prior join */
goto
done
;
if
(
gsf
->
gf_numsrc
)
{
newpsl
=
(
struct
ip6_sf_socklist
*
)
sock_kmalloc
(
sk
,
IP6_SFLSIZE
(
gsf
->
gf_numsrc
),
GFP_ATOMIC
);
if
(
!
newpsl
)
{
err
=
-
ENOBUFS
;
goto
done
;
}
newpsl
->
sl_max
=
newpsl
->
sl_count
=
gsf
->
gf_numsrc
;
for
(
i
=
0
;
i
<
newpsl
->
sl_count
;
++
i
)
{
struct
sockaddr_in6
*
psin6
;
psin6
=
(
struct
sockaddr_in6
*
)
&
gsf
->
gf_slist
[
i
];
newpsl
->
sl_addr
[
i
]
=
psin6
->
sin6_addr
;
}
err
=
ip6_mc_add_src
(
idev
,
group
,
gsf
->
gf_fmode
,
newpsl
->
sl_count
,
newpsl
->
sl_addr
,
0
);
if
(
err
)
{
sock_kfree_s
(
sk
,
newpsl
,
IP6_SFLSIZE
(
newpsl
->
sl_max
));
goto
done
;
}
}
else
newpsl
=
0
;
psl
=
pmc
->
sflist
;
if
(
psl
)
{
(
void
)
ip6_mc_del_src
(
idev
,
group
,
pmc
->
sfmode
,
psl
->
sl_count
,
psl
->
sl_addr
,
0
);
sock_kfree_s
(
sk
,
psl
,
IP6_SFLSIZE
(
psl
->
sl_max
));
}
else
(
void
)
ip6_mc_del_src
(
idev
,
group
,
pmc
->
sfmode
,
0
,
0
,
0
);
pmc
->
sflist
=
newpsl
;
pmc
->
sfmode
=
gsf
->
gf_fmode
;
done:
read_unlock_bh
(
&
idev
->
lock
);
in6_dev_put
(
idev
);
dev_put
(
dev
);
return
err
;
}
int
ip6_mc_msfget
(
struct
sock
*
sk
,
struct
group_filter
*
gsf
,
struct
group_filter
*
optval
,
int
*
optlen
)
{
int
err
,
i
,
count
,
copycount
;
struct
in6_addr
*
group
;
struct
ipv6_mc_socklist
*
pmc
;
struct
inet6_dev
*
idev
;
struct
net_device
*
dev
;
struct
ipv6_pinfo
*
inet6
=
inet6_sk
(
sk
);
struct
ip6_sf_socklist
*
psl
;
group
=
&
((
struct
sockaddr_in6
*
)
&
gsf
->
gf_group
)
->
sin6_addr
;
if
(
!
(
ipv6_addr_type
(
group
)
&
IPV6_ADDR_MULTICAST
))
return
-
EINVAL
;
idev
=
ip6_mc_find_dev
(
group
,
gsf
->
gf_interface
);
if
(
!
idev
)
return
-
ENODEV
;
dev
=
idev
->
dev
;
err
=
-
EADDRNOTAVAIL
;
for
(
pmc
=
inet6
->
ipv6_mc_list
;
pmc
;
pmc
=
pmc
->
next
)
{
if
(
pmc
->
ifindex
!=
gsf
->
gf_interface
)
continue
;
if
(
ipv6_addr_cmp
(
group
,
&
pmc
->
addr
)
==
0
)
break
;
}
if
(
!
pmc
)
/* must have a prior join */
goto
done
;
gsf
->
gf_fmode
=
pmc
->
sfmode
;
psl
=
pmc
->
sflist
;
count
=
psl
?
psl
->
sl_count
:
0
;
read_unlock_bh
(
&
idev
->
lock
);
in6_dev_put
(
idev
);
dev_put
(
dev
);
copycount
=
count
<
gsf
->
gf_numsrc
?
count
:
gsf
->
gf_numsrc
;
gsf
->
gf_numsrc
=
count
;
if
(
put_user
(
GROUP_FILTER_SIZE
(
copycount
),
optlen
)
||
copy_to_user
((
void
*
)
optval
,
gsf
,
GROUP_FILTER_SIZE
(
0
)))
{
return
-
EFAULT
;
}
for
(
i
=
0
;
i
<
copycount
;
i
++
)
{
struct
sockaddr_in6
*
psin6
;
struct
sockaddr_storage
ss
;
psin6
=
(
struct
sockaddr_in6
*
)
&
ss
;
memset
(
&
ss
,
0
,
sizeof
(
ss
));
psin6
->
sin6_family
=
AF_INET6
;
psin6
->
sin6_addr
=
psl
->
sl_addr
[
i
];
if
(
copy_to_user
((
void
*
)
&
optval
->
gf_slist
[
i
],
&
ss
,
sizeof
(
ss
)))
return
-
EFAULT
;
}
return
0
;
done:
read_unlock_bh
(
&
idev
->
lock
);
in6_dev_put
(
idev
);
dev_put
(
dev
);
return
err
;
}
int
inet6_mc_check
(
struct
sock
*
sk
,
struct
in6_addr
*
mc_addr
,
struct
in6_addr
*
src_addr
)
{
struct
ipv6_pinfo
*
np
=
inet6_sk
(
sk
);
struct
ipv6_mc_socklist
*
mc
;
struct
ip6_sf_socklist
*
psl
;
int
rv
=
0
;
read_lock
(
&
ipv6_sk_mc_lock
);
for
(
mc
=
np
->
ipv6_mc_list
;
mc
;
mc
=
mc
->
next
)
{
if
(
ipv6_addr_cmp
(
&
mc
->
addr
,
addr
)
==
0
)
{
read_unlock
(
&
ipv6_sk_mc_lock
);
return
1
;
if
(
ipv6_addr_cmp
(
&
mc
->
addr
,
mc_addr
)
==
0
)
break
;
}
if
(
!
mc
)
{
read_unlock
(
&
ipv6_sk_mc_lock
);
return
0
;
}
psl
=
mc
->
sflist
;
if
(
!
psl
)
{
rv
=
mc
->
sfmode
==
MCAST_EXCLUDE
;
}
else
{
int
i
;
for
(
i
=
0
;
i
<
psl
->
sl_count
;
i
++
)
{
if
(
ipv6_addr_cmp
(
&
psl
->
sl_addr
[
i
],
src_addr
)
==
0
)
break
;
}
rv
=
(
mc
->
sfmode
==
MCAST_INCLUDE
&&
i
<
psl
->
sl_count
);
rv
|=
(
mc
->
sfmode
==
MCAST_EXCLUDE
&&
i
>=
psl
->
sl_count
);
}
read_unlock
(
&
ipv6_sk_mc_lock
);
return
0
;
return
rv
;
}
static
void
ma_put
(
struct
ifmcaddr6
*
mc
)
...
...
@@ -216,7 +619,7 @@ static void ma_put(struct ifmcaddr6 *mc)
}
}
static
int
igmp6_group_added
(
struct
ifmcaddr6
*
mc
)
static
void
igmp6_group_added
(
struct
ifmcaddr6
*
mc
)
{
struct
net_device
*
dev
=
mc
->
idev
->
dev
;
char
buf
[
MAX_ADDR_LEN
];
...
...
@@ -229,12 +632,20 @@ static int igmp6_group_added(struct ifmcaddr6 *mc)
}
spin_unlock_bh
(
&
mc
->
mca_lock
);
if
(
dev
->
flags
&
IFF_UP
)
if
(
!
(
dev
->
flags
&
IFF_UP
)
||
(
mc
->
mca_flags
&
MAF_NOREPORT
))
return
;
if
(
MLD_V1_SEEN
(
mc
->
idev
))
{
igmp6_join_group
(
mc
);
return
0
;
return
;
}
/* else v2 */
mc
->
mca_crcount
=
mc
->
idev
->
mc_qrv
;
mld_ifc_event
(
mc
->
idev
);
}
static
int
igmp6_group_dropped
(
struct
ifmcaddr6
*
mc
)
static
void
igmp6_group_dropped
(
struct
ifmcaddr6
*
mc
)
{
struct
net_device
*
dev
=
mc
->
idev
->
dev
;
char
buf
[
MAX_ADDR_LEN
];
...
...
@@ -247,9 +658,122 @@ static int igmp6_group_dropped(struct ifmcaddr6 *mc)
}
spin_unlock_bh
(
&
mc
->
mca_lock
);
if
(
mc
->
mca_flags
&
MAF_NOREPORT
)
goto
done
;
if
(
dev
->
flags
&
IFF_UP
)
igmp6_leave_group
(
mc
);
return
0
;
spin_lock_bh
(
&
mc
->
mca_lock
);
if
(
del_timer
(
&
mc
->
mca_timer
))
atomic_dec
(
&
mc
->
mca_refcnt
);
spin_unlock_bh
(
&
mc
->
mca_lock
);
done:
ip6_mc_clear_src
(
mc
);
}
/*
* deleted ifmcaddr6 manipulation
*/
static
void
mld_add_delrec
(
struct
inet6_dev
*
idev
,
struct
ifmcaddr6
*
im
)
{
struct
ifmcaddr6
*
pmc
;
/* this is an "ifmcaddr6" for convenience; only the fields below
* are actually used. In particular, the refcnt and users are not
* used for management of the delete list. Using the same structure
* for deleted items allows change reports to use common code with
* non-deleted or query-response MCA's.
*/
pmc
=
(
struct
ifmcaddr6
*
)
kmalloc
(
sizeof
(
*
pmc
),
GFP_ATOMIC
);
if
(
!
pmc
)
return
;
memset
(
pmc
,
0
,
sizeof
(
*
pmc
));
spin_lock_bh
(
&
im
->
mca_lock
);
pmc
->
mca_lock
=
SPIN_LOCK_UNLOCKED
;
pmc
->
idev
=
im
->
idev
;
in6_dev_hold
(
idev
);
pmc
->
mca_addr
=
im
->
mca_addr
;
pmc
->
mca_crcount
=
idev
->
mc_qrv
;
pmc
->
mca_sfmode
=
im
->
mca_sfmode
;
if
(
pmc
->
mca_sfmode
==
MCAST_INCLUDE
)
{
struct
ip6_sf_list
*
psf
;
pmc
->
mca_tomb
=
im
->
mca_tomb
;
pmc
->
mca_sources
=
im
->
mca_sources
;
im
->
mca_tomb
=
im
->
mca_sources
=
0
;
for
(
psf
=
pmc
->
mca_sources
;
psf
;
psf
=
psf
->
sf_next
)
psf
->
sf_crcount
=
pmc
->
mca_crcount
;
}
spin_unlock_bh
(
&
im
->
mca_lock
);
write_lock_bh
(
&
idev
->
mc_lock
);
pmc
->
next
=
idev
->
mc_tomb
;
idev
->
mc_tomb
=
pmc
;
write_unlock_bh
(
&
idev
->
mc_lock
);
}
static
void
mld_del_delrec
(
struct
inet6_dev
*
idev
,
struct
in6_addr
*
pmca
)
{
struct
ifmcaddr6
*
pmc
,
*
pmc_prev
;
struct
ip6_sf_list
*
psf
,
*
psf_next
;
write_lock_bh
(
&
idev
->
mc_lock
);
pmc_prev
=
0
;
for
(
pmc
=
idev
->
mc_tomb
;
pmc
;
pmc
=
pmc
->
next
)
{
if
(
ipv6_addr_cmp
(
&
pmc
->
mca_addr
,
pmca
)
==
0
)
break
;
pmc_prev
=
pmc
;
}
if
(
pmc
)
{
if
(
pmc_prev
)
pmc_prev
->
next
=
pmc
->
next
;
else
idev
->
mc_tomb
=
pmc
->
next
;
}
write_unlock_bh
(
&
idev
->
mc_lock
);
if
(
pmc
)
{
for
(
psf
=
pmc
->
mca_tomb
;
psf
;
psf
=
psf_next
)
{
psf_next
=
psf
->
sf_next
;
kfree
(
psf
);
}
in6_dev_put
(
pmc
->
idev
);
kfree
(
pmc
);
}
}
static
void
mld_clear_delrec
(
struct
inet6_dev
*
idev
)
{
struct
ifmcaddr6
*
pmc
,
*
nextpmc
;
write_lock_bh
(
&
idev
->
mc_lock
);
pmc
=
idev
->
mc_tomb
;
idev
->
mc_tomb
=
0
;
write_unlock_bh
(
&
idev
->
mc_lock
);
for
(;
pmc
;
pmc
=
nextpmc
)
{
nextpmc
=
pmc
->
next
;
ip6_mc_clear_src
(
pmc
);
in6_dev_put
(
pmc
->
idev
);
kfree
(
pmc
);
}
/* clear dead sources, too */
read_lock_bh
(
&
idev
->
lock
);
for
(
pmc
=
idev
->
mc_list
;
pmc
;
pmc
=
pmc
->
next
)
{
struct
ip6_sf_list
*
psf
,
*
psf_next
;
spin_lock_bh
(
&
pmc
->
mca_lock
);
psf
=
pmc
->
mca_tomb
;
pmc
->
mca_tomb
=
0
;
spin_unlock_bh
(
&
pmc
->
mca_lock
);
for
(
psf
=
pmc
->
mca_tomb
;
psf
;
psf
=
psf_next
)
{
psf_next
=
psf
->
sf_next
;
kfree
(
psf
);
}
}
read_unlock_bh
(
&
idev
->
lock
);
}
...
...
@@ -277,6 +801,8 @@ int ipv6_dev_mc_inc(struct net_device *dev, struct in6_addr *addr)
if
(
ipv6_addr_cmp
(
&
mc
->
mca_addr
,
addr
)
==
0
)
{
mc
->
mca_users
++
;
write_unlock_bh
(
&
idev
->
lock
);
ip6_mc_add_src
(
idev
,
&
mc
->
mca_addr
,
MCAST_EXCLUDE
,
0
,
0
,
0
);
in6_dev_put
(
idev
);
return
0
;
}
...
...
@@ -305,10 +831,19 @@ int ipv6_dev_mc_inc(struct net_device *dev, struct in6_addr *addr)
atomic_set
(
&
mc
->
mca_refcnt
,
2
);
mc
->
mca_lock
=
SPIN_LOCK_UNLOCKED
;
/* initial mode is (EX, empty) */
mc
->
mca_sfmode
=
MCAST_EXCLUDE
;
mc
->
mca_sfcount
[
MCAST_EXCLUDE
]
=
1
;
if
(
ipv6_addr_is_ll_all_nodes
(
&
mc
->
mca_addr
)
||
IPV6_ADDR_MC_SCOPE
(
&
mc
->
mca_addr
)
<
IPV6_ADDR_SCOPE_LINKLOCAL
)
mc
->
mca_flags
|=
MAF_NOREPORT
;
mc
->
next
=
idev
->
mc_list
;
idev
->
mc_list
=
mc
;
write_unlock_bh
(
&
idev
->
lock
);
mld_del_delrec
(
idev
,
&
mc
->
mca_addr
);
igmp6_group_added
(
mc
);
ma_put
(
mc
);
return
0
;
...
...
@@ -353,25 +888,57 @@ int ipv6_dev_mc_dec(struct net_device *dev, struct in6_addr *addr)
/*
* check if the interface/address pair is valid
*/
int
ipv6_chk_mcast_addr
(
struct
net_device
*
dev
,
struct
in6_addr
*
addr
)
int
ipv6_chk_mcast_addr
(
struct
net_device
*
dev
,
struct
in6_addr
*
group
,
struct
in6_addr
*
src_addr
)
{
struct
inet6_dev
*
idev
;
struct
ifmcaddr6
*
mc
;
int
rv
=
0
;
idev
=
in6_dev_get
(
dev
);
if
(
idev
)
{
read_lock_bh
(
&
idev
->
lock
);
for
(
mc
=
idev
->
mc_list
;
mc
;
mc
=
mc
->
next
)
{
if
(
ipv6_addr_cmp
(
&
mc
->
mca_addr
,
addr
)
==
0
)
{
read_unlock_bh
(
&
idev
->
lock
);
in6_dev_put
(
idev
);
return
1
;
if
(
ipv6_addr_cmp
(
&
mc
->
mca_addr
,
group
)
==
0
)
break
;
}
if
(
mc
)
{
struct
ip6_sf_list
*
psf
;
spin_lock_bh
(
&
mc
->
mca_lock
);
for
(
psf
=
mc
->
mca_sources
;
psf
;
psf
=
psf
->
sf_next
)
{
if
(
ipv6_addr_cmp
(
&
psf
->
sf_addr
,
src_addr
)
==
0
)
break
;
}
if
(
psf
)
rv
=
psf
->
sf_count
[
MCAST_INCLUDE
]
||
psf
->
sf_count
[
MCAST_EXCLUDE
]
!=
mc
->
mca_sfcount
[
MCAST_EXCLUDE
];
else
rv
=
mc
->
mca_sfcount
[
MCAST_EXCLUDE
]
!=
0
;
spin_unlock_bh
(
&
mc
->
mca_lock
);
}
read_unlock_bh
(
&
idev
->
lock
);
in6_dev_put
(
idev
);
}
return
0
;
return
rv
;
}
static
void
mld_gq_start_timer
(
struct
inet6_dev
*
idev
)
{
int
tv
=
net_random
()
%
idev
->
mc_maxdelay
;
idev
->
mc_gq_running
=
1
;
if
(
!
mod_timer
(
&
idev
->
mc_gq_timer
,
jiffies
+
tv
+
2
))
atomic_inc
(
&
idev
->
refcnt
);
}
static
void
mld_ifc_start_timer
(
struct
inet6_dev
*
idev
,
int
delay
)
{
int
tv
=
net_random
()
%
delay
;
if
(
!
mod_timer
(
&
idev
->
mc_ifc_timer
,
jiffies
+
tv
+
2
))
atomic_inc
(
&
idev
->
refcnt
);
}
/*
...
...
@@ -386,7 +953,6 @@ static void igmp6_group_queried(struct ifmcaddr6 *ma, unsigned long resptime)
if
(
ipv6_addr_type
(
&
ma
->
mca_addr
)
&
(
IPV6_ADDR_LINKLOCAL
|
IPV6_ADDR_LOOPBACK
))
return
;
spin_lock
(
&
ma
->
mca_lock
);
if
(
del_timer
(
&
ma
->
mca_timer
))
{
atomic_dec
(
&
ma
->
mca_refcnt
);
delay
=
ma
->
mca_timer
.
expires
-
jiffies
;
...
...
@@ -398,60 +964,137 @@ static void igmp6_group_queried(struct ifmcaddr6 *ma, unsigned long resptime)
else
delay
=
1
;
}
ma
->
mca_timer
.
expires
=
jiffies
+
delay
;
if
(
!
mod_timer
(
&
ma
->
mca_timer
,
jiffies
+
delay
))
atomic_inc
(
&
ma
->
mca_refcnt
);
spin_unlock
(
&
ma
->
mca_lock
);
}
static
void
mld_marksources
(
struct
ifmcaddr6
*
pmc
,
int
nsrcs
,
struct
in6_addr
*
srcs
)
{
struct
ip6_sf_list
*
psf
;
int
i
,
scount
;
scount
=
0
;
for
(
psf
=
pmc
->
mca_sources
;
psf
;
psf
=
psf
->
sf_next
)
{
if
(
scount
==
nsrcs
)
break
;
for
(
i
=
0
;
i
<
nsrcs
;
i
++
)
if
(
ipv6_addr_cmp
(
&
srcs
[
i
],
&
psf
->
sf_addr
)
==
0
)
{
psf
->
sf_gsresp
=
1
;
scount
++
;
break
;
}
}
}
int
igmp6_event_query
(
struct
sk_buff
*
skb
)
{
struct
mld2_query
*
mlh2
=
(
struct
mld2_query
*
)
skb
->
h
.
raw
;
struct
ifmcaddr6
*
ma
;
struct
in6_addr
*
addr
p
;
unsigned
long
resptime
;
struct
in6_addr
*
grou
p
;
unsigned
long
max_delay
;
struct
inet6_dev
*
idev
;
struct
icmp6hdr
*
hdr
;
int
addr_type
;
int
group_type
;
int
mark
=
0
;
int
len
;
if
(
!
pskb_may_pull
(
skb
,
sizeof
(
struct
in6_addr
)))
return
-
EINVAL
;
hdr
=
(
struct
icmp6hdr
*
)
skb
->
h
.
raw
;
len
=
ntohs
(
skb
->
nh
.
ipv6h
->
payload_len
)
;
/* Drop queries with not link local source */
if
(
!
(
ipv6_addr_type
(
&
skb
->
nh
.
ipv6h
->
saddr
)
&
IPV6_ADDR_LINKLOCAL
))
return
-
EINVAL
;
resptime
=
ntohs
(
hdr
->
icmp6_maxdelay
);
/* Translate milliseconds to jiffies */
resptime
=
(
resptime
<<
10
)
/
(
1024000
/
HZ
);
addrp
=
(
struct
in6_addr
*
)
(
hdr
+
1
);
addr_type
=
ipv6_addr_type
(
addrp
);
if
(
addr_type
!=
IPV6_ADDR_ANY
&&
!
(
addr_type
&
IPV6_ADDR_MULTICAST
))
return
-
EINVAL
;
idev
=
in6_dev_get
(
skb
->
dev
);
if
(
idev
==
NULL
)
return
0
;
read_lock
(
&
idev
->
lock
);
if
(
addr_type
==
IPV6_ADDR_ANY
)
{
for
(
ma
=
idev
->
mc_list
;
ma
;
ma
=
ma
->
next
)
igmp6_group_queried
(
ma
,
resptime
);
hdr
=
(
struct
icmp6hdr
*
)
skb
->
h
.
raw
;
group
=
(
struct
in6_addr
*
)
(
hdr
+
1
);
group_type
=
ipv6_addr_type
(
group
);
if
(
group_type
!=
IPV6_ADDR_ANY
&&
!
(
group_type
&
IPV6_ADDR_MULTICAST
))
{
in6_dev_put
(
idev
);
return
-
EINVAL
;
}
if
(
len
==
24
)
{
int
switchback
;
/* MLDv1 router present */
/* Translate milliseconds to jiffies */
max_delay
=
ntohs
(
hdr
->
icmp6_maxdelay
)
*
(
HZ
/
10
);
switchback
=
(
idev
->
mc_qrv
+
1
)
*
max_delay
;
idev
->
mc_v1_seen
=
jiffies
+
switchback
;
/* cancel the interface change timer */
idev
->
mc_ifc_count
=
0
;
if
(
del_timer
(
&
idev
->
mc_ifc_timer
))
atomic_dec
(
&
idev
->
refcnt
);
/* clear deleted report items */
mld_clear_delrec
(
idev
);
}
else
if
(
len
>=
28
)
{
max_delay
=
MLDV2_MRC
(
ntohs
(
mlh2
->
mrc
))
*
(
HZ
/
10
);
idev
->
mc_maxdelay
=
max_delay
;
if
(
mlh2
->
qrv
)
idev
->
mc_qrv
=
mlh2
->
qrv
;
if
(
group_type
==
IPV6_ADDR_ANY
)
{
/* general query */
if
(
mlh2
->
nsrcs
)
{
in6_dev_put
(
idev
);
return
-
EINVAL
;
/* no sources allowed */
}
mld_gq_start_timer
(
idev
);
in6_dev_put
(
idev
);
return
0
;
}
/* mark sources to include, if group & source-specific */
mark
=
mlh2
->
nsrcs
!=
0
;
}
else
{
in6_dev_put
(
idev
);
return
-
EINVAL
;
}
read_lock_bh
(
&
idev
->
lock
);
if
(
group_type
==
IPV6_ADDR_ANY
)
{
for
(
ma
=
idev
->
mc_list
;
ma
;
ma
=
ma
->
next
)
{
spin_lock_bh
(
&
ma
->
mca_lock
);
igmp6_group_queried
(
ma
,
max_delay
);
spin_unlock_bh
(
&
ma
->
mca_lock
);
}
}
else
{
for
(
ma
=
idev
->
mc_list
;
ma
;
ma
=
ma
->
next
)
{
if
(
ipv6_addr_cmp
(
addrp
,
&
ma
->
mca_addr
)
==
0
)
{
igmp6_group_queried
(
ma
,
resptime
);
break
;
if
(
group_type
!=
IPV6_ADDR_ANY
&&
ipv6_addr_cmp
(
group
,
&
ma
->
mca_addr
)
!=
0
)
continue
;
spin_lock_bh
(
&
ma
->
mca_lock
);
if
(
ma
->
mca_flags
&
MAF_TIMER_RUNNING
)
{
/* gsquery <- gsquery && mark */
if
(
!
mark
)
ma
->
mca_flags
&=
~
MAF_GSQUERY
;
}
else
{
/* gsquery <- mark */
if
(
mark
)
ma
->
mca_flags
|=
MAF_GSQUERY
;
else
ma
->
mca_flags
&=
~
MAF_GSQUERY
;
}
if
(
ma
->
mca_flags
&
MAF_GSQUERY
)
mld_marksources
(
ma
,
ntohs
(
mlh2
->
nsrcs
),
mlh2
->
srcs
);
igmp6_group_queried
(
ma
,
max_delay
);
spin_unlock_bh
(
&
ma
->
mca_lock
);
if
(
group_type
!=
IPV6_ADDR_ANY
)
break
;
}
}
read_unlock
(
&
idev
->
lock
);
read_unlock
_bh
(
&
idev
->
lock
);
in6_dev_put
(
idev
);
return
0
;
...
...
@@ -491,7 +1134,7 @@ int igmp6_event_report(struct sk_buff *skb)
* Cancel the timer for this group
*/
read_lock
(
&
idev
->
lock
);
read_lock
_bh
(
&
idev
->
lock
);
for
(
ma
=
idev
->
mc_list
;
ma
;
ma
=
ma
->
next
)
{
if
(
ipv6_addr_cmp
(
&
ma
->
mca_addr
,
addrp
)
==
0
)
{
spin_lock
(
&
ma
->
mca_lock
);
...
...
@@ -502,11 +1145,376 @@ int igmp6_event_report(struct sk_buff *skb)
break
;
}
}
read_unlock
(
&
idev
->
lock
);
read_unlock
_bh
(
&
idev
->
lock
);
in6_dev_put
(
idev
);
return
0
;
}
static
int
is_in
(
struct
ifmcaddr6
*
pmc
,
struct
ip6_sf_list
*
psf
,
int
type
,
int
gdeleted
,
int
sdeleted
)
{
switch
(
type
)
{
case
MLD2_MODE_IS_INCLUDE
:
case
MLD2_MODE_IS_EXCLUDE
:
if
(
gdeleted
||
sdeleted
)
return
0
;
return
!
((
pmc
->
mca_flags
&
MAF_GSQUERY
)
&&
!
psf
->
sf_gsresp
);
case
MLD2_CHANGE_TO_INCLUDE
:
if
(
gdeleted
||
sdeleted
)
return
0
;
return
psf
->
sf_count
[
MCAST_INCLUDE
]
!=
0
;
case
MLD2_CHANGE_TO_EXCLUDE
:
if
(
gdeleted
||
sdeleted
)
return
0
;
if
(
pmc
->
mca_sfcount
[
MCAST_EXCLUDE
]
==
0
||
psf
->
sf_count
[
MCAST_INCLUDE
])
return
0
;
return
pmc
->
mca_sfcount
[
MCAST_EXCLUDE
]
==
psf
->
sf_count
[
MCAST_EXCLUDE
];
case
MLD2_ALLOW_NEW_SOURCES
:
if
(
gdeleted
||
!
psf
->
sf_crcount
)
return
0
;
return
(
pmc
->
mca_sfmode
==
MCAST_INCLUDE
)
^
sdeleted
;
case
MLD2_BLOCK_OLD_SOURCES
:
if
(
pmc
->
mca_sfmode
==
MCAST_INCLUDE
)
return
gdeleted
||
(
psf
->
sf_crcount
&&
sdeleted
);
return
psf
->
sf_crcount
&&
!
gdeleted
&&
!
sdeleted
;
}
return
0
;
}
static
int
mld_scount
(
struct
ifmcaddr6
*
pmc
,
int
type
,
int
gdeleted
,
int
sdeleted
)
{
struct
ip6_sf_list
*
psf
;
int
scount
=
0
;
for
(
psf
=
pmc
->
mca_sources
;
psf
;
psf
=
psf
->
sf_next
)
{
if
(
!
is_in
(
pmc
,
psf
,
type
,
gdeleted
,
sdeleted
))
continue
;
scount
++
;
}
return
scount
;
}
static
struct
sk_buff
*
mld_newpack
(
struct
net_device
*
dev
,
int
size
)
{
struct
sock
*
sk
=
igmp6_socket
->
sk
;
struct
sk_buff
*
skb
;
struct
mld2_report
*
pmr
;
struct
in6_addr
addr_buf
;
int
err
;
u8
ra
[
8
]
=
{
IPPROTO_ICMPV6
,
0
,
IPV6_TLV_ROUTERALERT
,
2
,
0
,
0
,
IPV6_TLV_PADN
,
0
};
skb
=
sock_alloc_send_skb
(
sk
,
size
+
dev
->
hard_header_len
+
15
,
0
,
&
err
);
if
(
skb
==
0
)
return
0
;
skb_reserve
(
skb
,
(
dev
->
hard_header_len
+
15
)
&
~
15
);
if
(
dev
->
hard_header
)
{
unsigned
char
ha
[
MAX_ADDR_LEN
];
ndisc_mc_map
(
&
mld2_all_mcr
,
ha
,
dev
,
1
);
if
(
dev
->
hard_header
(
skb
,
dev
,
ETH_P_IPV6
,
ha
,
NULL
,
size
)
<
0
)
{
kfree_skb
(
skb
);
return
0
;
}
}
if
(
ipv6_get_lladdr
(
dev
,
&
addr_buf
))
{
/* <draft-ietf-magma-mld-source-02.txt>:
* use unspecified address as the source address
* when a valid link-local address is not available.
*/
memset
(
&
addr_buf
,
0
,
sizeof
(
addr_buf
));
}
ip6_nd_hdr
(
sk
,
skb
,
dev
,
&
addr_buf
,
&
mld2_all_mcr
,
NEXTHDR_HOP
,
0
);
memcpy
(
skb_put
(
skb
,
sizeof
(
ra
)),
ra
,
sizeof
(
ra
));
pmr
=
(
struct
mld2_report
*
)
skb_put
(
skb
,
sizeof
(
*
pmr
));
skb
->
h
.
raw
=
(
unsigned
char
*
)
pmr
;
pmr
->
type
=
ICMPV6_MLD2_REPORT
;
pmr
->
resv1
=
0
;
pmr
->
csum
=
0
;
pmr
->
resv2
=
0
;
pmr
->
ngrec
=
0
;
return
skb
;
}
static
void
mld_sendpack
(
struct
sk_buff
*
skb
)
{
struct
ipv6hdr
*
pip6
=
skb
->
nh
.
ipv6h
;
struct
mld2_report
*
pmr
=
(
struct
mld2_report
*
)
skb
->
h
.
raw
;
int
payload_len
,
mldlen
;
payload_len
=
skb
->
tail
-
(
unsigned
char
*
)
skb
->
nh
.
ipv6h
-
sizeof
(
struct
ipv6hdr
);
mldlen
=
skb
->
tail
-
skb
->
h
.
raw
;
pip6
->
payload_len
=
htons
(
payload_len
);
pmr
->
csum
=
csum_ipv6_magic
(
&
pip6
->
saddr
,
&
pip6
->
daddr
,
mldlen
,
IPPROTO_ICMPV6
,
csum_partial
(
skb
->
h
.
raw
,
mldlen
,
0
));
dev_queue_xmit
(
skb
);
ICMP6_INC_STATS
(
Icmp6OutMsgs
);
}
static
int
grec_size
(
struct
ifmcaddr6
*
pmc
,
int
type
,
int
gdel
,
int
sdel
)
{
return
sizeof
(
struct
mld2_grec
)
+
4
*
mld_scount
(
pmc
,
type
,
gdel
,
sdel
);
}
static
struct
sk_buff
*
add_grhead
(
struct
sk_buff
*
skb
,
struct
ifmcaddr6
*
pmc
,
int
type
,
struct
mld2_grec
**
ppgr
)
{
struct
net_device
*
dev
=
pmc
->
idev
->
dev
;
struct
mld2_report
*
pmr
;
struct
mld2_grec
*
pgr
;
if
(
!
skb
)
skb
=
mld_newpack
(
dev
,
dev
->
mtu
);
if
(
!
skb
)
return
0
;
pgr
=
(
struct
mld2_grec
*
)
skb_put
(
skb
,
sizeof
(
struct
mld2_grec
));
pgr
->
grec_type
=
type
;
pgr
->
grec_auxwords
=
0
;
pgr
->
grec_nsrcs
=
0
;
pgr
->
grec_mca
=
pmc
->
mca_addr
;
/* structure copy */
pmr
=
(
struct
mld2_report
*
)
skb
->
h
.
raw
;
pmr
->
ngrec
=
htons
(
ntohs
(
pmr
->
ngrec
)
+
1
);
*
ppgr
=
pgr
;
return
skb
;
}
#define AVAILABLE(skb) ((skb) ? ((skb)->dev ? (skb)->dev->mtu - (skb)->len : \
skb_tailroom(skb)) : 0)
static
struct
sk_buff
*
add_grec
(
struct
sk_buff
*
skb
,
struct
ifmcaddr6
*
pmc
,
int
type
,
int
gdeleted
,
int
sdeleted
)
{
struct
net_device
*
dev
=
pmc
->
idev
->
dev
;
struct
mld2_report
*
pmr
;
struct
mld2_grec
*
pgr
=
0
;
struct
ip6_sf_list
*
psf
,
*
psf_next
,
*
psf_prev
,
*
psf_list
;
int
scount
,
first
,
isquery
,
truncate
;
if
(
pmc
->
mca_flags
&
MAF_NOREPORT
)
return
skb
;
isquery
=
type
==
MLD2_MODE_IS_INCLUDE
||
type
==
MLD2_MODE_IS_EXCLUDE
;
truncate
=
type
==
MLD2_MODE_IS_EXCLUDE
||
type
==
MLD2_CHANGE_TO_EXCLUDE
;
psf_list
=
sdeleted
?
pmc
->
mca_tomb
:
pmc
->
mca_sources
;
if
(
!
psf_list
)
{
if
(
type
==
MLD2_ALLOW_NEW_SOURCES
||
type
==
MLD2_BLOCK_OLD_SOURCES
)
return
skb
;
if
(
pmc
->
mca_crcount
||
isquery
)
skb
=
add_grhead
(
skb
,
pmc
,
type
,
&
pgr
);
return
skb
;
}
pmr
=
skb
?
(
struct
mld2_report
*
)
skb
->
h
.
raw
:
0
;
/* EX and TO_EX get a fresh packet, if needed */
if
(
truncate
)
{
if
(
pmr
&&
pmr
->
ngrec
&&
AVAILABLE
(
skb
)
<
grec_size
(
pmc
,
type
,
gdeleted
,
sdeleted
))
{
if
(
skb
)
mld_sendpack
(
skb
);
skb
=
mld_newpack
(
dev
,
dev
->
mtu
);
}
}
first
=
1
;
scount
=
0
;
psf_prev
=
0
;
for
(
psf
=
psf_list
;
psf
;
psf
=
psf_next
)
{
struct
in6_addr
*
psrc
;
psf_next
=
psf
->
sf_next
;
if
(
!
is_in
(
pmc
,
psf
,
type
,
gdeleted
,
sdeleted
))
{
psf_prev
=
psf
;
continue
;
}
/* clear marks on query responses */
if
(
isquery
)
psf
->
sf_gsresp
=
0
;
if
(
AVAILABLE
(
skb
)
<
sizeof
(
*
psrc
)
+
first
*
sizeof
(
struct
mld2_grec
))
{
if
(
truncate
&&
!
first
)
break
;
/* truncate these */
if
(
pgr
)
pgr
->
grec_nsrcs
=
htons
(
scount
);
if
(
skb
)
mld_sendpack
(
skb
);
skb
=
mld_newpack
(
dev
,
dev
->
mtu
);
first
=
1
;
scount
=
0
;
}
if
(
first
)
{
skb
=
add_grhead
(
skb
,
pmc
,
type
,
&
pgr
);
first
=
0
;
}
psrc
=
(
struct
in6_addr
*
)
skb_put
(
skb
,
sizeof
(
*
psrc
));
*
psrc
=
psf
->
sf_addr
;
scount
++
;
if
((
type
==
MLD2_ALLOW_NEW_SOURCES
||
type
==
MLD2_BLOCK_OLD_SOURCES
)
&&
psf
->
sf_crcount
)
{
psf
->
sf_crcount
--
;
if
((
sdeleted
||
gdeleted
)
&&
psf
->
sf_crcount
==
0
)
{
if
(
psf_prev
)
psf_prev
->
sf_next
=
psf
->
sf_next
;
else
pmc
->
mca_tomb
=
psf
->
sf_next
;
kfree
(
psf
);
continue
;
}
}
psf_prev
=
psf
;
}
if
(
pgr
)
pgr
->
grec_nsrcs
=
htons
(
scount
);
if
(
isquery
)
pmc
->
mca_flags
&=
~
MAF_GSQUERY
;
/* clear query state */
return
skb
;
}
static
void
mld_send_report
(
struct
inet6_dev
*
idev
,
struct
ifmcaddr6
*
pmc
)
{
struct
sk_buff
*
skb
=
0
;
int
type
;
if
(
!
pmc
)
{
read_lock_bh
(
&
idev
->
lock
);
for
(
pmc
=
idev
->
mc_list
;
pmc
;
pmc
=
pmc
->
next
)
{
if
(
pmc
->
mca_flags
&
MAF_NOREPORT
)
continue
;
spin_lock_bh
(
&
pmc
->
mca_lock
);
if
(
pmc
->
mca_sfcount
[
MCAST_EXCLUDE
])
type
=
MLD2_MODE_IS_EXCLUDE
;
else
type
=
MLD2_MODE_IS_INCLUDE
;
skb
=
add_grec
(
skb
,
pmc
,
type
,
0
,
0
);
spin_unlock_bh
(
&
pmc
->
mca_lock
);
}
read_unlock_bh
(
&
idev
->
lock
);
}
else
{
spin_lock_bh
(
&
pmc
->
mca_lock
);
if
(
pmc
->
mca_sfcount
[
MCAST_EXCLUDE
])
type
=
MLD2_MODE_IS_EXCLUDE
;
else
type
=
MLD2_MODE_IS_INCLUDE
;
skb
=
add_grec
(
skb
,
pmc
,
type
,
0
,
0
);
spin_unlock_bh
(
&
pmc
->
mca_lock
);
}
if
(
skb
)
mld_sendpack
(
skb
);
}
/*
* remove zero-count source records from a source filter list
*/
static
void
mld_clear_zeros
(
struct
ip6_sf_list
**
ppsf
)
{
struct
ip6_sf_list
*
psf_prev
,
*
psf_next
,
*
psf
;
psf_prev
=
0
;
for
(
psf
=*
ppsf
;
psf
;
psf
=
psf_next
)
{
psf_next
=
psf
->
sf_next
;
if
(
psf
->
sf_crcount
==
0
)
{
if
(
psf_prev
)
psf_prev
->
sf_next
=
psf
->
sf_next
;
else
*
ppsf
=
psf
->
sf_next
;
kfree
(
psf
);
}
else
psf_prev
=
psf
;
}
}
static
void
mld_send_cr
(
struct
inet6_dev
*
idev
)
{
struct
ifmcaddr6
*
pmc
,
*
pmc_prev
,
*
pmc_next
;
struct
sk_buff
*
skb
=
0
;
int
type
,
dtype
;
read_lock_bh
(
&
idev
->
lock
);
write_lock_bh
(
&
idev
->
mc_lock
);
/* deleted MCA's */
pmc_prev
=
0
;
for
(
pmc
=
idev
->
mc_tomb
;
pmc
;
pmc
=
pmc_next
)
{
pmc_next
=
pmc
->
next
;
if
(
pmc
->
mca_sfmode
==
MCAST_INCLUDE
)
{
type
=
MLD2_BLOCK_OLD_SOURCES
;
dtype
=
MLD2_BLOCK_OLD_SOURCES
;
skb
=
add_grec
(
skb
,
pmc
,
type
,
1
,
0
);
skb
=
add_grec
(
skb
,
pmc
,
dtype
,
1
,
1
);
}
if
(
pmc
->
mca_crcount
)
{
pmc
->
mca_crcount
--
;
if
(
pmc
->
mca_sfmode
==
MCAST_EXCLUDE
)
{
type
=
MLD2_CHANGE_TO_INCLUDE
;
skb
=
add_grec
(
skb
,
pmc
,
type
,
1
,
0
);
}
if
(
pmc
->
mca_crcount
==
0
)
{
mld_clear_zeros
(
&
pmc
->
mca_tomb
);
mld_clear_zeros
(
&
pmc
->
mca_sources
);
}
}
if
(
pmc
->
mca_crcount
==
0
&&
!
pmc
->
mca_tomb
&&
!
pmc
->
mca_sources
)
{
if
(
pmc_prev
)
pmc_prev
->
next
=
pmc_next
;
else
idev
->
mc_tomb
=
pmc_next
;
in6_dev_put
(
pmc
->
idev
);
kfree
(
pmc
);
}
else
pmc_prev
=
pmc
;
}
write_unlock_bh
(
&
idev
->
mc_lock
);
/* change recs */
for
(
pmc
=
idev
->
mc_list
;
pmc
;
pmc
=
pmc
->
next
)
{
spin_lock_bh
(
&
pmc
->
mca_lock
);
if
(
pmc
->
mca_sfcount
[
MCAST_EXCLUDE
])
{
type
=
MLD2_BLOCK_OLD_SOURCES
;
dtype
=
MLD2_ALLOW_NEW_SOURCES
;
}
else
{
type
=
MLD2_ALLOW_NEW_SOURCES
;
dtype
=
MLD2_BLOCK_OLD_SOURCES
;
}
skb
=
add_grec
(
skb
,
pmc
,
type
,
0
,
0
);
skb
=
add_grec
(
skb
,
pmc
,
dtype
,
0
,
1
);
/* deleted sources */
/* filter mode changes */
if
(
pmc
->
mca_crcount
)
{
pmc
->
mca_crcount
--
;
if
(
pmc
->
mca_sfmode
==
MCAST_EXCLUDE
)
type
=
MLD2_CHANGE_TO_EXCLUDE
;
else
type
=
MLD2_CHANGE_TO_INCLUDE
;
skb
=
add_grec
(
skb
,
pmc
,
type
,
0
,
0
);
}
spin_unlock_bh
(
&
pmc
->
mca_lock
);
}
read_unlock_bh
(
&
idev
->
lock
);
if
(
!
skb
)
return
;
(
void
)
mld_sendpack
(
skb
);
}
static
void
igmp6_send
(
struct
in6_addr
*
addr
,
struct
net_device
*
dev
,
int
type
)
{
struct
sock
*
sk
=
igmp6_socket
->
sk
;
...
...
@@ -579,12 +1587,251 @@ static void igmp6_send(struct in6_addr *addr, struct net_device *dev, int type)
kfree_skb
(
skb
);
}
static
int
ip6_mc_del1_src
(
struct
ifmcaddr6
*
pmc
,
int
sfmode
,
struct
in6_addr
*
psfsrc
)
{
struct
ip6_sf_list
*
psf
,
*
psf_prev
;
int
rv
=
0
;
psf_prev
=
0
;
for
(
psf
=
pmc
->
mca_sources
;
psf
;
psf
=
psf
->
sf_next
)
{
if
(
ipv6_addr_cmp
(
&
psf
->
sf_addr
,
psfsrc
)
==
0
)
break
;
psf_prev
=
psf
;
}
if
(
!
psf
||
psf
->
sf_count
[
sfmode
]
==
0
)
{
/* source filter not found, or count wrong => bug */
return
-
ESRCH
;
}
psf
->
sf_count
[
sfmode
]
--
;
if
(
!
psf
->
sf_count
[
MCAST_INCLUDE
]
&&
!
psf
->
sf_count
[
MCAST_EXCLUDE
])
{
struct
inet6_dev
*
idev
=
pmc
->
idev
;
/* no more filters for this source */
if
(
psf_prev
)
psf_prev
->
sf_next
=
psf
->
sf_next
;
else
pmc
->
mca_sources
=
psf
->
sf_next
;
if
(
psf
->
sf_oldin
&&
!
(
pmc
->
mca_flags
&
MAF_NOREPORT
)
&&
!
MLD_V1_SEEN
(
idev
))
{
psf
->
sf_crcount
=
idev
->
mc_qrv
;
psf
->
sf_next
=
pmc
->
mca_tomb
;
pmc
->
mca_tomb
=
psf
;
rv
=
1
;
}
else
kfree
(
psf
);
}
return
rv
;
}
int
ip6_mc_del_src
(
struct
inet6_dev
*
idev
,
struct
in6_addr
*
pmca
,
int
sfmode
,
int
sfcount
,
struct
in6_addr
*
psfsrc
,
int
delta
)
{
struct
ifmcaddr6
*
pmc
;
int
changerec
=
0
;
int
i
,
err
;
if
(
!
idev
)
return
-
ENODEV
;
read_lock_bh
(
&
idev
->
lock
);
for
(
pmc
=
idev
->
mc_list
;
pmc
;
pmc
=
pmc
->
next
)
{
if
(
ipv6_addr_cmp
(
pmca
,
&
pmc
->
mca_addr
)
==
0
)
break
;
}
if
(
!
pmc
)
{
/* MCA not found?? bug */
read_unlock_bh
(
&
idev
->
lock
);
return
-
ESRCH
;
}
spin_lock_bh
(
&
pmc
->
mca_lock
);
read_unlock_bh
(
&
idev
->
lock
);
sf_markstate
(
pmc
);
if
(
!
delta
)
{
if
(
!
pmc
->
mca_sfcount
[
sfmode
])
{
spin_unlock_bh
(
&
pmc
->
mca_lock
);
return
-
EINVAL
;
}
pmc
->
mca_sfcount
[
sfmode
]
--
;
}
err
=
0
;
for
(
i
=
0
;
i
<
sfcount
;
i
++
)
{
int
rv
=
ip6_mc_del1_src
(
pmc
,
sfmode
,
&
psfsrc
[
i
]);
changerec
|=
rv
>
0
;
if
(
!
err
&&
rv
<
0
)
err
=
rv
;
}
if
(
pmc
->
mca_sfmode
==
MCAST_EXCLUDE
&&
pmc
->
mca_sfcount
[
MCAST_EXCLUDE
]
==
0
&&
pmc
->
mca_sfcount
[
MCAST_INCLUDE
])
{
struct
ip6_sf_list
*
psf
;
/* filter mode change */
pmc
->
mca_sfmode
=
MCAST_INCLUDE
;
pmc
->
mca_crcount
=
idev
->
mc_qrv
;
idev
->
mc_ifc_count
=
pmc
->
mca_crcount
;
for
(
psf
=
pmc
->
mca_sources
;
psf
;
psf
=
psf
->
sf_next
)
psf
->
sf_crcount
=
0
;
mld_ifc_event
(
pmc
->
idev
);
}
else
if
(
sf_setstate
(
pmc
)
||
changerec
)
mld_ifc_event
(
pmc
->
idev
);
spin_unlock_bh
(
&
pmc
->
mca_lock
);
return
err
;
}
/*
* Add multicast single-source filter to the interface list
*/
static
int
ip6_mc_add1_src
(
struct
ifmcaddr6
*
pmc
,
int
sfmode
,
struct
in6_addr
*
psfsrc
,
int
delta
)
{
struct
ip6_sf_list
*
psf
,
*
psf_prev
;
psf_prev
=
0
;
for
(
psf
=
pmc
->
mca_sources
;
psf
;
psf
=
psf
->
sf_next
)
{
if
(
ipv6_addr_cmp
(
&
psf
->
sf_addr
,
psfsrc
)
==
0
)
break
;
psf_prev
=
psf
;
}
if
(
!
psf
)
{
psf
=
(
struct
ip6_sf_list
*
)
kmalloc
(
sizeof
(
*
psf
),
GFP_ATOMIC
);
if
(
!
psf
)
return
-
ENOBUFS
;
memset
(
psf
,
0
,
sizeof
(
*
psf
));
psf
->
sf_addr
=
*
psfsrc
;
if
(
psf_prev
)
{
psf_prev
->
sf_next
=
psf
;
}
else
pmc
->
mca_sources
=
psf
;
}
psf
->
sf_count
[
sfmode
]
++
;
return
0
;
}
static
void
sf_markstate
(
struct
ifmcaddr6
*
pmc
)
{
struct
ip6_sf_list
*
psf
;
int
mca_xcount
=
pmc
->
mca_sfcount
[
MCAST_EXCLUDE
];
for
(
psf
=
pmc
->
mca_sources
;
psf
;
psf
=
psf
->
sf_next
)
if
(
pmc
->
mca_sfcount
[
MCAST_EXCLUDE
])
{
psf
->
sf_oldin
=
mca_xcount
==
psf
->
sf_count
[
MCAST_EXCLUDE
]
&&
!
psf
->
sf_count
[
MCAST_INCLUDE
];
}
else
psf
->
sf_oldin
=
psf
->
sf_count
[
MCAST_INCLUDE
]
!=
0
;
}
static
int
sf_setstate
(
struct
ifmcaddr6
*
pmc
)
{
struct
ip6_sf_list
*
psf
;
int
mca_xcount
=
pmc
->
mca_sfcount
[
MCAST_EXCLUDE
];
int
qrv
=
pmc
->
idev
->
mc_qrv
;
int
new_in
,
rv
;
rv
=
0
;
for
(
psf
=
pmc
->
mca_sources
;
psf
;
psf
=
psf
->
sf_next
)
{
if
(
pmc
->
mca_sfcount
[
MCAST_EXCLUDE
])
{
new_in
=
mca_xcount
==
psf
->
sf_count
[
MCAST_EXCLUDE
]
&&
!
psf
->
sf_count
[
MCAST_INCLUDE
];
}
else
new_in
=
psf
->
sf_count
[
MCAST_INCLUDE
]
!=
0
;
if
(
new_in
!=
psf
->
sf_oldin
)
{
psf
->
sf_crcount
=
qrv
;
rv
++
;
}
}
return
rv
;
}
/*
* Add multicast source filter list to the interface list
*/
int
ip6_mc_add_src
(
struct
inet6_dev
*
idev
,
struct
in6_addr
*
pmca
,
int
sfmode
,
int
sfcount
,
struct
in6_addr
*
psfsrc
,
int
delta
)
{
struct
ifmcaddr6
*
pmc
;
int
isexclude
;
int
i
,
err
;
if
(
!
idev
)
return
-
ENODEV
;
read_lock_bh
(
&
idev
->
lock
);
for
(
pmc
=
idev
->
mc_list
;
pmc
;
pmc
=
pmc
->
next
)
{
if
(
ipv6_addr_cmp
(
pmca
,
&
pmc
->
mca_addr
)
==
0
)
break
;
}
if
(
!
pmc
)
{
/* MCA not found?? bug */
read_unlock_bh
(
&
idev
->
lock
);
return
-
ESRCH
;
}
spin_lock_bh
(
&
pmc
->
mca_lock
);
read_unlock_bh
(
&
idev
->
lock
);
sf_markstate
(
pmc
);
isexclude
=
pmc
->
mca_sfmode
==
MCAST_EXCLUDE
;
if
(
!
delta
)
pmc
->
mca_sfcount
[
sfmode
]
++
;
err
=
0
;
for
(
i
=
0
;
i
<
sfcount
;
i
++
)
{
err
=
ip6_mc_add1_src
(
pmc
,
sfmode
,
&
psfsrc
[
i
],
delta
);
if
(
err
)
break
;
}
if
(
err
)
{
int
j
;
pmc
->
mca_sfcount
[
sfmode
]
--
;
for
(
j
=
0
;
j
<
i
;
j
++
)
(
void
)
ip6_mc_del1_src
(
pmc
,
sfmode
,
&
psfsrc
[
i
]);
}
else
if
(
isexclude
!=
(
pmc
->
mca_sfcount
[
MCAST_EXCLUDE
]
!=
0
))
{
struct
inet6_dev
*
idev
=
pmc
->
idev
;
struct
ip6_sf_list
*
psf
;
/* filter mode change */
if
(
pmc
->
mca_sfcount
[
MCAST_EXCLUDE
])
pmc
->
mca_sfmode
=
MCAST_EXCLUDE
;
else
if
(
pmc
->
mca_sfcount
[
MCAST_INCLUDE
])
pmc
->
mca_sfmode
=
MCAST_INCLUDE
;
/* else no filters; keep old mode for reports */
pmc
->
mca_crcount
=
idev
->
mc_qrv
;
idev
->
mc_ifc_count
=
pmc
->
mca_crcount
;
for
(
psf
=
pmc
->
mca_sources
;
psf
;
psf
=
psf
->
sf_next
)
psf
->
sf_crcount
=
0
;
mld_ifc_event
(
idev
);
}
else
if
(
sf_setstate
(
pmc
))
mld_ifc_event
(
idev
);
spin_unlock_bh
(
&
pmc
->
mca_lock
);
return
err
;
}
static
void
ip6_mc_clear_src
(
struct
ifmcaddr6
*
pmc
)
{
struct
ip6_sf_list
*
psf
,
*
nextpsf
;
for
(
psf
=
pmc
->
mca_tomb
;
psf
;
psf
=
nextpsf
)
{
nextpsf
=
psf
->
sf_next
;
kfree
(
psf
);
}
pmc
->
mca_tomb
=
0
;
for
(
psf
=
pmc
->
mca_sources
;
psf
;
psf
=
nextpsf
)
{
nextpsf
=
psf
->
sf_next
;
kfree
(
psf
);
}
pmc
->
mca_sources
=
0
;
pmc
->
mca_sfmode
=
MCAST_EXCLUDE
;
pmc
->
mca_sfcount
[
MCAST_EXCLUDE
]
=
0
;
pmc
->
mca_sfcount
[
MCAST_EXCLUDE
]
=
1
;
}
static
void
igmp6_join_group
(
struct
ifmcaddr6
*
ma
)
{
unsigned
long
delay
;
if
(
IPV6_ADDR_MC_SCOPE
(
&
ma
->
mca_addr
)
<
IPV6_ADDR_SCOPE_LINKLOCAL
||
ipv6_addr_is_ll_all_nodes
(
&
ma
->
mca_addr
))
if
(
ma
->
mca_flags
&
MAF_NOREPORT
)
return
;
igmp6_send
(
&
ma
->
mca_addr
,
ma
->
idev
->
dev
,
ICMPV6_MGM_REPORT
);
...
...
@@ -603,26 +1850,71 @@ static void igmp6_join_group(struct ifmcaddr6 *ma)
spin_unlock_bh
(
&
ma
->
mca_lock
);
}
int
ip6_mc_leave_src
(
struct
sock
*
sk
,
struct
ipv6_mc_socklist
*
iml
,
struct
inet6_dev
*
idev
)
{
int
err
;
if
(
iml
->
sflist
==
0
)
{
/* any-source empty exclude case */
return
ip6_mc_del_src
(
idev
,
&
iml
->
addr
,
iml
->
sfmode
,
0
,
0
,
0
);
}
err
=
ip6_mc_del_src
(
idev
,
&
iml
->
addr
,
iml
->
sfmode
,
iml
->
sflist
->
sl_count
,
iml
->
sflist
->
sl_addr
,
0
);
sock_kfree_s
(
sk
,
iml
->
sflist
,
IP6_SFLSIZE
(
iml
->
sflist
->
sl_max
));
iml
->
sflist
=
0
;
return
err
;
}
static
void
igmp6_leave_group
(
struct
ifmcaddr6
*
ma
)
{
if
(
IPV6_ADDR_MC_SCOPE
(
&
ma
->
mca_addr
)
<
IPV6_ADDR_SCOPE_LINKLOCAL
||
ipv6_addr_is_ll_all_nodes
(
&
ma
->
mca_addr
))
return
;
if
(
MLD_V1_SEEN
(
ma
->
idev
))
{
if
(
ma
->
mca_flags
&
MAF_LAST_REPORTER
)
igmp6_send
(
&
ma
->
mca_addr
,
ma
->
idev
->
dev
,
ICMPV6_MGM_REDUCTION
);
}
else
{
mld_add_delrec
(
ma
->
idev
,
ma
);
mld_ifc_event
(
ma
->
idev
);
}
}
if
(
ma
->
mca_flags
&
MAF_LAST_REPORTER
)
igmp6_send
(
&
ma
->
mca_addr
,
ma
->
idev
->
dev
,
ICMPV6_MGM_REDUCTION
);
static
void
mld_gq_timer_expire
(
unsigned
long
data
)
{
struct
inet6_dev
*
idev
=
(
struct
inet6_dev
*
)
data
;
spin_lock_bh
(
&
ma
->
mca_lock
);
if
(
del_timer
(
&
ma
->
mca_timer
))
atomic_dec
(
&
ma
->
mca_refcnt
);
spin_unlock_bh
(
&
ma
->
mca_lock
);
idev
->
mc_gq_running
=
0
;
mld_send_report
(
idev
,
0
);
}
static
void
mld_ifc_timer_expire
(
unsigned
long
data
)
{
struct
inet6_dev
*
idev
=
(
struct
inet6_dev
*
)
data
;
mld_send_cr
(
idev
);
if
(
idev
->
mc_ifc_count
)
{
idev
->
mc_ifc_count
--
;
if
(
idev
->
mc_ifc_count
)
mld_ifc_start_timer
(
idev
,
idev
->
mc_maxdelay
);
}
}
static
void
mld_ifc_event
(
struct
inet6_dev
*
idev
)
{
if
(
MLD_V1_SEEN
(
idev
))
return
;
idev
->
mc_ifc_count
=
idev
->
mc_qrv
;
mld_ifc_start_timer
(
idev
,
1
);
}
static
void
igmp6_timer_handler
(
unsigned
long
data
)
{
struct
ifmcaddr6
*
ma
=
(
struct
ifmcaddr6
*
)
data
;
igmp6_send
(
&
ma
->
mca_addr
,
ma
->
idev
->
dev
,
ICMPV6_MGM_REPORT
);
if
(
MLD_V1_SEEN
(
ma
->
idev
))
igmp6_send
(
&
ma
->
mca_addr
,
ma
->
idev
->
dev
,
ICMPV6_MGM_REPORT
);
else
mld_send_report
(
ma
->
idev
,
ma
);
spin_lock
(
&
ma
->
mca_lock
);
ma
->
mca_flags
|=
MAF_LAST_REPORTER
;
...
...
@@ -640,9 +1932,18 @@ void ipv6_mc_down(struct inet6_dev *idev)
/* Withdraw multicast list */
read_lock_bh
(
&
idev
->
lock
);
idev
->
mc_ifc_count
=
0
;
if
(
del_timer
(
&
idev
->
mc_ifc_timer
))
atomic_dec
(
&
idev
->
refcnt
);
idev
->
mc_gq_running
=
0
;
if
(
del_timer
(
&
idev
->
mc_gq_timer
))
atomic_dec
(
&
idev
->
refcnt
);
for
(
i
=
idev
->
mc_list
;
i
;
i
=
i
->
next
)
igmp6_group_dropped
(
i
);
read_unlock_bh
(
&
idev
->
lock
);
mld_clear_delrec
(
idev
);
}
...
...
@@ -666,6 +1967,22 @@ void ipv6_mc_init_dev(struct inet6_dev *idev)
{
struct
in6_addr
maddr
;
write_lock_bh
(
&
idev
->
lock
);
idev
->
mc_lock
=
RW_LOCK_UNLOCKED
;
idev
->
mc_gq_running
=
0
;
init_timer
(
&
idev
->
mc_gq_timer
);
idev
->
mc_gq_timer
.
data
=
(
unsigned
long
)
idev
;
idev
->
mc_gq_timer
.
function
=
&
mld_gq_timer_expire
;
idev
->
mc_tomb
=
0
;
idev
->
mc_ifc_count
=
0
;
init_timer
(
&
idev
->
mc_ifc_timer
);
idev
->
mc_ifc_timer
.
data
=
(
unsigned
long
)
idev
;
idev
->
mc_ifc_timer
.
function
=
&
mld_ifc_timer_expire
;
idev
->
mc_qrv
=
MLD_QRV_DEFAULT
;
idev
->
mc_maxdelay
=
IGMP6_UNSOLICITED_IVAL
;
idev
->
mc_v1_seen
=
0
;
write_unlock_bh
(
&
idev
->
lock
);
/* Add all-nodes address. */
ipv6_addr_all_nodes
(
&
maddr
);
ipv6_dev_mc_inc
(
idev
->
dev
,
&
maddr
);
...
...
@@ -744,6 +2061,113 @@ static int igmp6_read_proc(char *buffer, char **start, off_t offset,
}
*
eof
=
1
;
done:
read_unlock
(
&
dev_base_lock
);
*
start
=
buffer
+
(
offset
-
begin
);
len
-=
(
offset
-
begin
);
if
(
len
>
length
)
len
=
length
;
if
(
len
<
0
)
len
=
0
;
return
len
;
}
static
int
ip6_mcf_read_proc
(
char
*
buffer
,
char
**
start
,
off_t
offset
,
int
length
,
int
*
eof
,
void
*
data
)
{
off_t
pos
=
0
,
begin
=
0
;
int
len
=
0
;
int
first
=
1
;
struct
net_device
*
dev
;
read_lock
(
&
dev_base_lock
);
for
(
dev
=
dev_base
;
dev
;
dev
=
dev
->
next
)
{
struct
inet6_dev
*
idev
=
in6_dev_get
(
dev
);
struct
ifmcaddr6
*
imc
;
if
(
idev
==
NULL
)
continue
;
read_lock_bh
(
&
idev
->
lock
);
for
(
imc
=
idev
->
mc_list
;
imc
;
imc
=
imc
->
next
)
{
struct
ip6_sf_list
*
psf
;
unsigned
long
icount
,
xcount
,
i
;
spin_lock_bh
(
&
imc
->
mca_lock
);
icount
=
imc
->
mca_sfcount
[
MCAST_INCLUDE
];
xcount
=
imc
->
mca_sfcount
[
MCAST_EXCLUDE
];
for
(
psf
=
imc
->
mca_sources
;
psf
;
psf
=
psf
->
sf_next
)
{
if
(
first
)
{
len
+=
sprintf
(
buffer
+
len
,
"%3s %6s "
"%32s %32s %6s %6s
\n
"
,
"Idx"
,
"Device"
,
"Multicast Address"
,
"Source Address"
,
"INC"
,
"EXC"
);
first
=
0
;
}
len
+=
sprintf
(
buffer
+
len
,
"%3d %6.6s "
,
dev
->
ifindex
,
dev
->
name
);
for
(
i
=
0
;
i
<
16
;
i
++
)
len
+=
sprintf
(
buffer
+
len
,
"%02x"
,
imc
->
mca_addr
.
s6_addr
[
i
]);
buffer
[
len
++
]
=
' '
;
for
(
i
=
0
;
i
<
16
;
i
++
)
len
+=
sprintf
(
buffer
+
len
,
"%02x"
,
psf
->
sf_addr
.
s6_addr
[
i
]);
len
+=
sprintf
(
buffer
+
len
,
" %6lu %6lu
\n
"
,
psf
->
sf_count
[
MCAST_INCLUDE
],
psf
->
sf_count
[
MCAST_EXCLUDE
]);
pos
=
begin
+
len
;
if
(
pos
<
offset
)
{
len
=
0
;
begin
=
pos
;
}
if
(
pos
>
offset
+
length
)
{
spin_unlock_bh
(
&
imc
->
mca_lock
);
read_unlock_bh
(
&
idev
->
lock
);
in6_dev_put
(
idev
);
goto
done
;
}
icount
-=
psf
->
sf_count
[
MCAST_INCLUDE
];
xcount
-=
psf
->
sf_count
[
MCAST_EXCLUDE
];
}
if
(
icount
>
0
||
xcount
>
0
)
{
if
(
first
)
{
len
+=
sprintf
(
buffer
+
len
,
"%3s %6s "
"%32s %32s %6s %6s
\n
"
,
"Idx"
,
"Device"
,
"Multicast Address"
,
"Source Address"
,
"INC"
,
"EXC"
);
first
=
0
;
}
len
+=
sprintf
(
buffer
+
len
,
"%3d %6.6s "
,
dev
->
ifindex
,
dev
->
name
);
for
(
i
=
0
;
i
<
16
;
i
++
)
len
+=
sprintf
(
buffer
+
len
,
"%02x"
,
imc
->
mca_addr
.
s6_addr
[
i
]);
len
+=
sprintf
(
buffer
+
len
,
" %32s %6lu %6lu
\n
"
,
"NONE"
,
icount
,
xcount
);
pos
=
begin
+
len
;
if
(
pos
<
offset
)
{
len
=
0
;
begin
=
pos
;
}
if
(
pos
>
offset
+
length
)
{
spin_unlock_bh
(
&
imc
->
mca_lock
);
read_unlock_bh
(
&
idev
->
lock
);
in6_dev_put
(
idev
);
goto
done
;
}
}
spin_unlock_bh
(
&
imc
->
mca_lock
);
}
read_unlock_bh
(
&
idev
->
lock
);
in6_dev_put
(
idev
);
}
*
eof
=
1
;
done:
read_unlock
(
&
dev_base_lock
);
...
...
@@ -780,6 +2204,7 @@ int __init igmp6_init(struct net_proto_family *ops)
np
->
hop_limit
=
1
;
#ifdef CONFIG_PROC_FS
create_proc_read_entry
(
"net/igmp6"
,
0
,
0
,
igmp6_read_proc
,
NULL
);
create_proc_read_entry
(
"net/mcfilter6"
,
0
,
0
,
ip6_mcf_read_proc
,
NULL
);
#endif
return
0
;
...
...
net/ipv6/raw.c
View file @
06da9228
...
...
@@ -99,7 +99,7 @@ struct sock *__raw_v6_lookup(struct sock *sk, unsigned short num,
if
(
!
ipv6_addr_cmp
(
&
np
->
rcv_saddr
,
loc_addr
))
break
;
if
((
addr_type
&
IPV6_ADDR_MULTICAST
)
&&
inet6_mc_check
(
s
,
loc_addr
))
inet6_mc_check
(
s
,
loc_addr
,
rmt_addr
))
break
;
continue
;
}
...
...
net/ipv6/udp.c
View file @
06da9228
...
...
@@ -592,7 +592,7 @@ static struct sock *udp_v6_mcast_next(struct sock *sk,
if
(
!
ipv6_addr_cmp
(
&
np
->
rcv_saddr
,
loc_addr
))
return
s
;
}
if
(
!
inet6_mc_check
(
s
,
loc_addr
))
if
(
!
inet6_mc_check
(
s
,
loc_addr
,
rmt_addr
))
continue
;
return
s
;
}
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment