x/net/ipv4: add support for source-specific multicast

This CL introduces methods for the manipulation of source-specifc
group into both PacketConn and RawConn as follows:

JoinSourceSpecificGroup(*net.Interface, net.Addr, net.Addr) error
LeaveSourceSpecificGroup(*net.Interface, net.Addr, net.Addr) error
ExcludeSourceSpecificGroup(*net.Interface, net.Addr, net.Addr) error
IncludeSourceSpecificGroup(*net.Interface, net.Addr, net.Addr) error

Fixes golang/go#8266.

LGTM=iant
R=iant
CC=golang-codereviews
https://golang.org/cl/174030043
diff --git a/ipv4/dgramopt_posix.go b/ipv4/dgramopt_posix.go
index fce881a..68e25e2 100644
--- a/ipv4/dgramopt_posix.go
+++ b/ipv4/dgramopt_posix.go
@@ -93,7 +93,11 @@
 	return setInt(fd, &sockOpts[ssoMulticastLoopback], boolint(on))
 }
 
-// JoinGroup joins the group address group on the interface ifi.
+// JoinGroup joins the group address group on the interface ifi. By
+// default all sources that can cast data to group are accepted. It's
+// possible to mute and unmute data transmission from a specific
+// source by using ExcludeSourceSpecificGroup and
+// IncludeSourceSpecificGroup.
 // It uses the system assigned multicast interface when ifi is nil,
 // although this is not recommended because the assignment depends on
 // platforms and sometimes it might require routing configuration.
@@ -113,6 +117,8 @@
 }
 
 // LeaveGroup leaves the group address group on the interface ifi.
+// It's allowed to leave the group which is formed by
+// JoinSourceSpecificGroup for convenience.
 func (c *dgramOpt) LeaveGroup(ifi *net.Interface, group net.Addr) error {
 	if !c.ok() {
 		return syscall.EINVAL
@@ -127,3 +133,91 @@
 	}
 	return setGroup(fd, &sockOpts[ssoLeaveGroup], ifi, grp)
 }
+
+// JoinSourceSpecificGroup joins the source-specific group consisting
+// group and source on the interface ifi. It uses the system assigned
+// multicast interface when ifi is nil, although this is not
+// recommended because the assignment depends on platforms and
+// sometimes it might require routing configuration.
+func (c *dgramOpt) JoinSourceSpecificGroup(ifi *net.Interface, group, source net.Addr) error {
+	if !c.ok() {
+		return syscall.EINVAL
+	}
+	fd, err := c.sysfd()
+	if err != nil {
+		return err
+	}
+	grp := netAddrToIP4(group)
+	if grp == nil {
+		return errMissingAddress
+	}
+	src := netAddrToIP4(source)
+	if src == nil {
+		return errMissingAddress
+	}
+	return setSourceGroup(fd, &sockOpts[ssoJoinSourceGroup], ifi, grp, src)
+}
+
+// LeaveSourceSpecificGroup leaves the source-specific group on the
+// interface ifi.
+func (c *dgramOpt) LeaveSourceSpecificGroup(ifi *net.Interface, group, source net.Addr) error {
+	if !c.ok() {
+		return syscall.EINVAL
+	}
+	fd, err := c.sysfd()
+	if err != nil {
+		return err
+	}
+	grp := netAddrToIP4(group)
+	if grp == nil {
+		return errMissingAddress
+	}
+	src := netAddrToIP4(source)
+	if src == nil {
+		return errMissingAddress
+	}
+	return setSourceGroup(fd, &sockOpts[ssoLeaveSourceGroup], ifi, grp, src)
+}
+
+// ExcludeSourceSpecificGroup excludes the source-specific group from
+// the already joined groups by either JoinGroup or
+// JoinSourceSpecificGroup on the interface ifi.
+func (c *dgramOpt) ExcludeSourceSpecificGroup(ifi *net.Interface, group, source net.Addr) error {
+	if !c.ok() {
+		return syscall.EINVAL
+	}
+	fd, err := c.sysfd()
+	if err != nil {
+		return err
+	}
+	grp := netAddrToIP4(group)
+	if grp == nil {
+		return errMissingAddress
+	}
+	src := netAddrToIP4(source)
+	if src == nil {
+		return errMissingAddress
+	}
+	return setSourceGroup(fd, &sockOpts[ssoBlockSourceGroup], ifi, grp, src)
+}
+
+// IncludeSourceSpecificGroup includes the excluded source-specific
+// group by ExcludeSourceSpecificGroup again on the interface ifi.
+func (c *dgramOpt) IncludeSourceSpecificGroup(ifi *net.Interface, group, source net.Addr) error {
+	if !c.ok() {
+		return syscall.EINVAL
+	}
+	fd, err := c.sysfd()
+	if err != nil {
+		return err
+	}
+	grp := netAddrToIP4(group)
+	if grp == nil {
+		return errMissingAddress
+	}
+	src := netAddrToIP4(source)
+	if src == nil {
+		return errMissingAddress
+	}
+	return setSourceGroup(fd, &sockOpts[ssoUnblockSourceGroup], ifi, grp, src)
+}