Commit 06da9228 authored by David Stevens's avatar David Stevens Committed by David S. Miller

[IPV6]: Add MLDv2 support.

parent 98fab1e4
...@@ -86,6 +86,19 @@ struct icmp6hdr { ...@@ -86,6 +86,19 @@ struct icmp6hdr {
#define ICMPV6_MGM_REPORT 131 #define ICMPV6_MGM_REPORT 131
#define ICMPV6_MGM_REDUCTION 132 #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 * Codes for Destination Unreachable
*/ */
......
...@@ -90,6 +90,13 @@ struct in_addr { ...@@ -90,6 +90,13 @@ struct in_addr {
#define IP_ADD_SOURCE_MEMBERSHIP 39 #define IP_ADD_SOURCE_MEMBERSHIP 39
#define IP_DROP_SOURCE_MEMBERSHIP 40 #define IP_DROP_SOURCE_MEMBERSHIP 40
#define IP_MSFILTER 41 #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_EXCLUDE 0
#define MCAST_INCLUDE 1 #define MCAST_INCLUDE 1
...@@ -131,6 +138,32 @@ struct ip_msfilter { ...@@ -131,6 +138,32 @@ struct ip_msfilter {
(sizeof(struct ip_msfilter) - sizeof(__u32) \ (sizeof(struct ip_msfilter) - sizeof(__u32) \
+ (numsrc) * 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 struct in_pktinfo
{ {
int ipi_ifindex; int ipi_ifindex;
......
...@@ -79,6 +79,7 @@ ...@@ -79,6 +79,7 @@
#define IPOPT_TS_PRESPEC 3 /* specified modules only */ #define IPOPT_TS_PRESPEC 3 /* specified modules only */
#ifdef __KERNEL__ #ifdef __KERNEL__
#include <linux/config.h>
#include <linux/types.h> #include <linux/types.h>
#include <net/sock.h> #include <net/sock.h>
#include <linux/igmp.h> #include <linux/igmp.h>
......
...@@ -17,6 +17,7 @@ ...@@ -17,6 +17,7 @@
#ifndef _LINUX_TCP_H #ifndef _LINUX_TCP_H
#define _LINUX_TCP_H #define _LINUX_TCP_H
#include <linux/config.h>
#include <linux/skbuff.h> #include <linux/skbuff.h>
#include <asm/byteorder.h> #include <asm/byteorder.h>
#include <net/sock.h> #include <net/sock.h>
......
...@@ -17,6 +17,7 @@ ...@@ -17,6 +17,7 @@
#ifndef _LINUX_UDP_H #ifndef _LINUX_UDP_H
#define _LINUX_UDP_H #define _LINUX_UDP_H
#include <linux/config.h>
#include <asm/byteorder.h> #include <asm/byteorder.h>
#include <net/sock.h> #include <net/sock.h>
#include <linux/ip.h> #include <linux/ip.h>
......
...@@ -76,49 +76,38 @@ extern void addrconf_leave_solict(struct net_device *dev, ...@@ -76,49 +76,38 @@ extern void addrconf_leave_solict(struct net_device *dev,
/* /*
* multicast prototypes (mcast.c) * multicast prototypes (mcast.c)
*/ */
extern int ipv6_sock_mc_join(struct sock *sk, extern int ipv6_sock_mc_join(struct sock *sk, int ifindex,
int ifindex, struct in6_addr *addr);
struct in6_addr *addr); extern int ipv6_sock_mc_drop(struct sock *sk, int ifindex,
extern int ipv6_sock_mc_drop(struct sock *sk, struct in6_addr *addr);
int ifindex, extern void ipv6_sock_mc_close(struct sock *sk);
struct in6_addr *addr); extern int inet6_mc_check(struct sock *sk, struct in6_addr *mc_addr,
extern void ipv6_sock_mc_close(struct sock *sk); struct in6_addr *src_addr);
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_inc(struct net_device *dev, extern int ipv6_dev_mc_dec(struct net_device *dev, struct in6_addr *addr);
struct in6_addr *addr); extern void ipv6_mc_up(struct inet6_dev *idev);
extern int ipv6_dev_mc_dec(struct net_device *dev, extern void ipv6_mc_down(struct inet6_dev *idev);
struct in6_addr *addr); extern void ipv6_mc_init_dev(struct inet6_dev *idev);
extern void ipv6_mc_up(struct inet6_dev *idev); extern void ipv6_mc_destroy_dev(struct inet6_dev *idev);
extern void ipv6_mc_down(struct inet6_dev *idev); extern void addrconf_dad_failure(struct inet6_ifaddr *ifp);
extern void ipv6_mc_init_dev(struct inet6_dev *idev);
extern void ipv6_mc_destroy_dev(struct inet6_dev *idev); extern int ipv6_chk_mcast_addr(struct net_device *dev, struct in6_addr *group,
extern void addrconf_dad_failure(struct inet6_ifaddr *ifp); struct in6_addr *src_addr);
extern int ipv6_chk_mcast_addr(struct net_device *dev, extern void addrconf_prefix_rcv(struct net_device *dev, u8 *opt, int len);
struct in6_addr *addr);
extern void addrconf_prefix_rcv(struct net_device *dev,
u8 *opt, int len);
/* /*
* anycast prototypes (anycast.c) * anycast prototypes (anycast.c)
*/ */
extern int ipv6_sock_ac_join(struct sock *sk, extern int ipv6_sock_ac_join(struct sock *sk,int ifindex,struct in6_addr *addr);
int ifindex, extern int ipv6_sock_ac_drop(struct sock *sk,int ifindex,struct in6_addr *addr);
struct in6_addr *addr); extern void ipv6_sock_ac_close(struct sock *sk);
extern int ipv6_sock_ac_drop(struct sock *sk, extern int inet6_ac_check(struct sock *sk, struct in6_addr *addr, int ifindex);
int ifindex,
struct in6_addr *addr); extern int ipv6_dev_ac_inc(struct net_device *dev, struct in6_addr *addr);
extern void ipv6_sock_ac_close(struct sock *sk); extern int ipv6_dev_ac_dec(struct net_device *dev, struct in6_addr *addr);
extern int inet6_ac_check(struct sock *sk, struct in6_addr *addr, int ifindex); extern int ipv6_chk_acast_addr(struct net_device *dev, struct in6_addr *addr);
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 */ /* Device notifier */
......
...@@ -18,6 +18,7 @@ ...@@ -18,6 +18,7 @@
#ifndef _ICMP_H #ifndef _ICMP_H
#define _ICMP_H #define _ICMP_H
#include <linux/config.h>
#include <linux/icmp.h> #include <linux/icmp.h>
#include <linux/skbuff.h> #include <linux/skbuff.h>
......
...@@ -52,27 +52,58 @@ struct inet6_ifaddr ...@@ -52,27 +52,58 @@ struct inet6_ifaddr
int dead; 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 ipv6_mc_socklist
{ {
struct in6_addr addr; struct in6_addr addr;
int ifindex; int ifindex;
struct ipv6_mc_socklist *next; 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_TIMER_RUNNING 0x01
#define MAF_LAST_REPORTER 0x02 #define MAF_LAST_REPORTER 0x02
#define MAF_LOADED 0x04 #define MAF_LOADED 0x04
#define MAF_NOREPORT 0x08
#define MAF_GSQUERY 0x10
struct ifmcaddr6 struct ifmcaddr6
{ {
struct in6_addr mca_addr; struct in6_addr mca_addr;
struct inet6_dev *idev; struct inet6_dev *idev;
struct ifmcaddr6 *next; 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; struct timer_list mca_timer;
unsigned mca_flags; unsigned mca_flags;
int mca_users; int mca_users;
atomic_t mca_refcnt; atomic_t mca_refcnt;
spinlock_t mca_lock; spinlock_t mca_lock;
unsigned char mca_crcount;
}; };
/* Anycast stuff */ /* Anycast stuff */
...@@ -126,7 +157,18 @@ struct inet6_dev ...@@ -126,7 +157,18 @@ struct inet6_dev
struct net_device *dev; struct net_device *dev;
struct inet6_ifaddr *addr_list; struct inet6_ifaddr *addr_list;
struct ifmcaddr6 *mc_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; struct ifacaddr6 *ac_list;
rwlock_t lock; rwlock_t lock;
atomic_t refcnt; atomic_t refcnt;
......
...@@ -217,7 +217,7 @@ int ip6_mc_input(struct sk_buff *skb) ...@@ -217,7 +217,7 @@ int ip6_mc_input(struct sk_buff *skb)
IP6_INC_STATS_BH(Ip6InMcastPkts); IP6_INC_STATS_BH(Ip6InMcastPkts);
hdr = skb->nh.ipv6h; 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; deliver = 1;
/* /*
......
...@@ -110,7 +110,8 @@ int ip6_output(struct sk_buff *skb) ...@@ -110,7 +110,8 @@ int ip6_output(struct sk_buff *skb)
struct ipv6_pinfo* np = skb->sk ? inet6_sk(skb->sk) : NULL; struct ipv6_pinfo* np = skb->sk ? inet6_sk(skb->sk) : NULL;
if (!(dev->flags & IFF_LOOPBACK) && (!np || np->mc_loop) && 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); struct sk_buff *newskb = skb_clone(skb, GFP_ATOMIC);
/* Do not check for IFF_ALLMULTI; multicast routing /* Do not check for IFF_ALLMULTI; multicast routing
......
...@@ -19,6 +19,10 @@ ...@@ -19,6 +19,10 @@
* o Return -EINVAL for setsockopt of short lengths * o Return -EINVAL for setsockopt of short lengths
* o Truncate getsockopt returns * o Truncate getsockopt returns
* o Return an optlen of the truncated length if need be * 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> #include <linux/module.h>
...@@ -375,6 +379,89 @@ int ipv6_setsockopt(struct sock *sk, int level, int optname, char *optval, ...@@ -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); retv = ipv6_sock_ac_drop(sk, mreq.ipv6mr_ifindex, &mreq.ipv6mr_acaddr);
break; 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: case IPV6_ROUTER_ALERT:
retv = ip6_ra_control(sk, val, NULL); retv = ip6_ra_control(sk, val, NULL);
break; break;
...@@ -448,6 +535,21 @@ int ipv6_getsockopt(struct sock *sk, int level, int optname, char *optval, ...@@ -448,6 +535,21 @@ int ipv6_getsockopt(struct sock *sk, int level, int optname, char *optval,
return -ENOTCONN; return -ENOTCONN;
val = sk->family; val = sk->family;
break; 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: case IPV6_PKTOPTIONS:
{ {
......
This diff is collapsed.
...@@ -99,7 +99,7 @@ struct sock *__raw_v6_lookup(struct sock *sk, unsigned short num, ...@@ -99,7 +99,7 @@ struct sock *__raw_v6_lookup(struct sock *sk, unsigned short num,
if (!ipv6_addr_cmp(&np->rcv_saddr, loc_addr)) if (!ipv6_addr_cmp(&np->rcv_saddr, loc_addr))
break; break;
if ((addr_type & IPV6_ADDR_MULTICAST) && if ((addr_type & IPV6_ADDR_MULTICAST) &&
inet6_mc_check(s, loc_addr)) inet6_mc_check(s, loc_addr, rmt_addr))
break; break;
continue; continue;
} }
......
...@@ -592,7 +592,7 @@ static struct sock *udp_v6_mcast_next(struct sock *sk, ...@@ -592,7 +592,7 @@ static struct sock *udp_v6_mcast_next(struct sock *sk,
if (!ipv6_addr_cmp(&np->rcv_saddr, loc_addr)) if (!ipv6_addr_cmp(&np->rcv_saddr, loc_addr))
return s; return s;
} }
if(!inet6_mc_check(s, loc_addr)) if(!inet6_mc_check(s, loc_addr, rmt_addr))
continue; continue;
return s; return s;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment