diff options
Diffstat (limited to 'net/ipv4/igmp.c')
-rw-r--r-- | net/ipv4/igmp.c | 96 |
1 files changed, 55 insertions, 41 deletions
diff --git a/net/ipv4/igmp.c b/net/ipv4/igmp.c index 1f3183168a90..5088f90835ae 100644 --- a/net/ipv4/igmp.c +++ b/net/ipv4/igmp.c | |||
@@ -1615,9 +1615,10 @@ int ip_mc_join_group(struct sock *sk , struct ip_mreqn *imr) | |||
1615 | { | 1615 | { |
1616 | int err; | 1616 | int err; |
1617 | u32 addr = imr->imr_multiaddr.s_addr; | 1617 | u32 addr = imr->imr_multiaddr.s_addr; |
1618 | struct ip_mc_socklist *iml, *i; | 1618 | struct ip_mc_socklist *iml=NULL, *i; |
1619 | struct in_device *in_dev; | 1619 | struct in_device *in_dev; |
1620 | struct inet_sock *inet = inet_sk(sk); | 1620 | struct inet_sock *inet = inet_sk(sk); |
1621 | int ifindex; | ||
1621 | int count = 0; | 1622 | int count = 0; |
1622 | 1623 | ||
1623 | if (!MULTICAST(addr)) | 1624 | if (!MULTICAST(addr)) |
@@ -1633,37 +1634,30 @@ int ip_mc_join_group(struct sock *sk , struct ip_mreqn *imr) | |||
1633 | goto done; | 1634 | goto done; |
1634 | } | 1635 | } |
1635 | 1636 | ||
1636 | iml = (struct ip_mc_socklist *)sock_kmalloc(sk, sizeof(*iml), GFP_KERNEL); | ||
1637 | |||
1638 | err = -EADDRINUSE; | 1637 | err = -EADDRINUSE; |
1638 | ifindex = imr->imr_ifindex; | ||
1639 | for (i = inet->mc_list; i; i = i->next) { | 1639 | for (i = inet->mc_list; i; i = i->next) { |
1640 | if (memcmp(&i->multi, imr, sizeof(*imr)) == 0) { | 1640 | if (i->multi.imr_multiaddr.s_addr == addr && |
1641 | /* New style additions are reference counted */ | 1641 | i->multi.imr_ifindex == ifindex) |
1642 | if (imr->imr_address.s_addr == 0) { | ||
1643 | i->count++; | ||
1644 | err = 0; | ||
1645 | } | ||
1646 | goto done; | 1642 | goto done; |
1647 | } | ||
1648 | count++; | 1643 | count++; |
1649 | } | 1644 | } |
1650 | err = -ENOBUFS; | 1645 | err = -ENOBUFS; |
1651 | if (iml == NULL || count >= sysctl_igmp_max_memberships) | 1646 | if (count >= sysctl_igmp_max_memberships) |
1647 | goto done; | ||
1648 | iml = (struct ip_mc_socklist *)sock_kmalloc(sk,sizeof(*iml),GFP_KERNEL); | ||
1649 | if (iml == NULL) | ||
1652 | goto done; | 1650 | goto done; |
1651 | |||
1653 | memcpy(&iml->multi, imr, sizeof(*imr)); | 1652 | memcpy(&iml->multi, imr, sizeof(*imr)); |
1654 | iml->next = inet->mc_list; | 1653 | iml->next = inet->mc_list; |
1655 | iml->count = 1; | ||
1656 | iml->sflist = NULL; | 1654 | iml->sflist = NULL; |
1657 | iml->sfmode = MCAST_EXCLUDE; | 1655 | iml->sfmode = MCAST_EXCLUDE; |
1658 | inet->mc_list = iml; | 1656 | inet->mc_list = iml; |
1659 | ip_mc_inc_group(in_dev, addr); | 1657 | ip_mc_inc_group(in_dev, addr); |
1660 | iml = NULL; | ||
1661 | err = 0; | 1658 | err = 0; |
1662 | |||
1663 | done: | 1659 | done: |
1664 | rtnl_shunlock(); | 1660 | rtnl_shunlock(); |
1665 | if (iml) | ||
1666 | sock_kfree_s(sk, iml, sizeof(*iml)); | ||
1667 | return err; | 1661 | return err; |
1668 | } | 1662 | } |
1669 | 1663 | ||
@@ -1693,30 +1687,25 @@ int ip_mc_leave_group(struct sock *sk, struct ip_mreqn *imr) | |||
1693 | { | 1687 | { |
1694 | struct inet_sock *inet = inet_sk(sk); | 1688 | struct inet_sock *inet = inet_sk(sk); |
1695 | struct ip_mc_socklist *iml, **imlp; | 1689 | struct ip_mc_socklist *iml, **imlp; |
1690 | struct in_device *in_dev; | ||
1691 | u32 group = imr->imr_multiaddr.s_addr; | ||
1692 | u32 ifindex; | ||
1696 | 1693 | ||
1697 | rtnl_lock(); | 1694 | rtnl_lock(); |
1695 | in_dev = ip_mc_find_dev(imr); | ||
1696 | if (!in_dev) { | ||
1697 | rtnl_unlock(); | ||
1698 | return -ENODEV; | ||
1699 | } | ||
1700 | ifindex = imr->imr_ifindex; | ||
1698 | for (imlp = &inet->mc_list; (iml = *imlp) != NULL; imlp = &iml->next) { | 1701 | for (imlp = &inet->mc_list; (iml = *imlp) != NULL; imlp = &iml->next) { |
1699 | if (iml->multi.imr_multiaddr.s_addr==imr->imr_multiaddr.s_addr && | 1702 | if (iml->multi.imr_multiaddr.s_addr == group && |
1700 | iml->multi.imr_address.s_addr==imr->imr_address.s_addr && | 1703 | iml->multi.imr_ifindex == ifindex) { |
1701 | (!imr->imr_ifindex || iml->multi.imr_ifindex==imr->imr_ifindex)) { | 1704 | (void) ip_mc_leave_src(sk, iml, in_dev); |
1702 | struct in_device *in_dev; | ||
1703 | |||
1704 | in_dev = inetdev_by_index(iml->multi.imr_ifindex); | ||
1705 | if (in_dev) | ||
1706 | (void) ip_mc_leave_src(sk, iml, in_dev); | ||
1707 | if (--iml->count) { | ||
1708 | rtnl_unlock(); | ||
1709 | if (in_dev) | ||
1710 | in_dev_put(in_dev); | ||
1711 | return 0; | ||
1712 | } | ||
1713 | 1705 | ||
1714 | *imlp = iml->next; | 1706 | *imlp = iml->next; |
1715 | 1707 | ||
1716 | if (in_dev) { | 1708 | ip_mc_dec_group(in_dev, group); |
1717 | ip_mc_dec_group(in_dev, imr->imr_multiaddr.s_addr); | ||
1718 | in_dev_put(in_dev); | ||
1719 | } | ||
1720 | rtnl_unlock(); | 1709 | rtnl_unlock(); |
1721 | sock_kfree_s(sk, iml, sizeof(*iml)); | 1710 | sock_kfree_s(sk, iml, sizeof(*iml)); |
1722 | return 0; | 1711 | return 0; |
@@ -1736,6 +1725,7 @@ int ip_mc_source(int add, int omode, struct sock *sk, struct | |||
1736 | struct in_device *in_dev = NULL; | 1725 | struct in_device *in_dev = NULL; |
1737 | struct inet_sock *inet = inet_sk(sk); | 1726 | struct inet_sock *inet = inet_sk(sk); |
1738 | struct ip_sf_socklist *psl; | 1727 | struct ip_sf_socklist *psl; |
1728 | int leavegroup = 0; | ||
1739 | int i, j, rv; | 1729 | int i, j, rv; |
1740 | 1730 | ||
1741 | if (!MULTICAST(addr)) | 1731 | if (!MULTICAST(addr)) |
@@ -1755,15 +1745,20 @@ int ip_mc_source(int add, int omode, struct sock *sk, struct | |||
1755 | err = -EADDRNOTAVAIL; | 1745 | err = -EADDRNOTAVAIL; |
1756 | 1746 | ||
1757 | for (pmc=inet->mc_list; pmc; pmc=pmc->next) { | 1747 | for (pmc=inet->mc_list; pmc; pmc=pmc->next) { |
1758 | if (memcmp(&pmc->multi, mreqs, 2*sizeof(__u32)) == 0) | 1748 | if (pmc->multi.imr_multiaddr.s_addr == imr.imr_multiaddr.s_addr |
1749 | && pmc->multi.imr_ifindex == imr.imr_ifindex) | ||
1759 | break; | 1750 | break; |
1760 | } | 1751 | } |
1761 | if (!pmc) /* must have a prior join */ | 1752 | if (!pmc) { /* must have a prior join */ |
1753 | err = -EINVAL; | ||
1762 | goto done; | 1754 | goto done; |
1755 | } | ||
1763 | /* if a source filter was set, must be the same mode as before */ | 1756 | /* if a source filter was set, must be the same mode as before */ |
1764 | if (pmc->sflist) { | 1757 | if (pmc->sflist) { |
1765 | if (pmc->sfmode != omode) | 1758 | if (pmc->sfmode != omode) { |
1759 | err = -EINVAL; | ||
1766 | goto done; | 1760 | goto done; |
1761 | } | ||
1767 | } else if (pmc->sfmode != omode) { | 1762 | } else if (pmc->sfmode != omode) { |
1768 | /* allow mode switches for empty-set filters */ | 1763 | /* allow mode switches for empty-set filters */ |
1769 | ip_mc_add_src(in_dev, &mreqs->imr_multiaddr, omode, 0, NULL, 0); | 1764 | ip_mc_add_src(in_dev, &mreqs->imr_multiaddr, omode, 0, NULL, 0); |
@@ -1775,7 +1770,7 @@ int ip_mc_source(int add, int omode, struct sock *sk, struct | |||
1775 | psl = pmc->sflist; | 1770 | psl = pmc->sflist; |
1776 | if (!add) { | 1771 | if (!add) { |
1777 | if (!psl) | 1772 | if (!psl) |
1778 | goto done; | 1773 | goto done; /* err = -EADDRNOTAVAIL */ |
1779 | rv = !0; | 1774 | rv = !0; |
1780 | for (i=0; i<psl->sl_count; i++) { | 1775 | for (i=0; i<psl->sl_count; i++) { |
1781 | rv = memcmp(&psl->sl_addr[i], &mreqs->imr_sourceaddr, | 1776 | rv = memcmp(&psl->sl_addr[i], &mreqs->imr_sourceaddr, |
@@ -1784,7 +1779,13 @@ int ip_mc_source(int add, int omode, struct sock *sk, struct | |||
1784 | break; | 1779 | break; |
1785 | } | 1780 | } |
1786 | if (rv) /* source not found */ | 1781 | if (rv) /* source not found */ |
1782 | goto done; /* err = -EADDRNOTAVAIL */ | ||
1783 | |||
1784 | /* special case - (INCLUDE, empty) == LEAVE_GROUP */ | ||
1785 | if (psl->sl_count == 1 && omode == MCAST_INCLUDE) { | ||
1786 | leavegroup = 1; | ||
1787 | goto done; | 1787 | goto done; |
1788 | } | ||
1788 | 1789 | ||
1789 | /* update the interface filter */ | 1790 | /* update the interface filter */ |
1790 | ip_mc_del_src(in_dev, &mreqs->imr_multiaddr, omode, 1, | 1791 | ip_mc_del_src(in_dev, &mreqs->imr_multiaddr, omode, 1, |
@@ -1842,18 +1843,21 @@ int ip_mc_source(int add, int omode, struct sock *sk, struct | |||
1842 | &mreqs->imr_sourceaddr, 1); | 1843 | &mreqs->imr_sourceaddr, 1); |
1843 | done: | 1844 | done: |
1844 | rtnl_shunlock(); | 1845 | rtnl_shunlock(); |
1846 | if (leavegroup) | ||
1847 | return ip_mc_leave_group(sk, &imr); | ||
1845 | return err; | 1848 | return err; |
1846 | } | 1849 | } |
1847 | 1850 | ||
1848 | int ip_mc_msfilter(struct sock *sk, struct ip_msfilter *msf, int ifindex) | 1851 | int ip_mc_msfilter(struct sock *sk, struct ip_msfilter *msf, int ifindex) |
1849 | { | 1852 | { |
1850 | int err; | 1853 | int err = 0; |
1851 | struct ip_mreqn imr; | 1854 | struct ip_mreqn imr; |
1852 | u32 addr = msf->imsf_multiaddr; | 1855 | u32 addr = msf->imsf_multiaddr; |
1853 | struct ip_mc_socklist *pmc; | 1856 | struct ip_mc_socklist *pmc; |
1854 | struct in_device *in_dev; | 1857 | struct in_device *in_dev; |
1855 | struct inet_sock *inet = inet_sk(sk); | 1858 | struct inet_sock *inet = inet_sk(sk); |
1856 | struct ip_sf_socklist *newpsl, *psl; | 1859 | struct ip_sf_socklist *newpsl, *psl; |
1860 | int leavegroup = 0; | ||
1857 | 1861 | ||
1858 | if (!MULTICAST(addr)) | 1862 | if (!MULTICAST(addr)) |
1859 | return -EINVAL; | 1863 | return -EINVAL; |
@@ -1872,15 +1876,22 @@ int ip_mc_msfilter(struct sock *sk, struct ip_msfilter *msf, int ifindex) | |||
1872 | err = -ENODEV; | 1876 | err = -ENODEV; |
1873 | goto done; | 1877 | goto done; |
1874 | } | 1878 | } |
1875 | err = -EADDRNOTAVAIL; | 1879 | |
1880 | /* special case - (INCLUDE, empty) == LEAVE_GROUP */ | ||
1881 | if (msf->imsf_fmode == MCAST_INCLUDE && msf->imsf_numsrc == 0) { | ||
1882 | leavegroup = 1; | ||
1883 | goto done; | ||
1884 | } | ||
1876 | 1885 | ||
1877 | for (pmc=inet->mc_list; pmc; pmc=pmc->next) { | 1886 | for (pmc=inet->mc_list; pmc; pmc=pmc->next) { |
1878 | if (pmc->multi.imr_multiaddr.s_addr == msf->imsf_multiaddr && | 1887 | if (pmc->multi.imr_multiaddr.s_addr == msf->imsf_multiaddr && |
1879 | pmc->multi.imr_ifindex == imr.imr_ifindex) | 1888 | pmc->multi.imr_ifindex == imr.imr_ifindex) |
1880 | break; | 1889 | break; |
1881 | } | 1890 | } |
1882 | if (!pmc) /* must have a prior join */ | 1891 | if (!pmc) { /* must have a prior join */ |
1892 | err = -EINVAL; | ||
1883 | goto done; | 1893 | goto done; |
1894 | } | ||
1884 | if (msf->imsf_numsrc) { | 1895 | if (msf->imsf_numsrc) { |
1885 | newpsl = (struct ip_sf_socklist *)sock_kmalloc(sk, | 1896 | newpsl = (struct ip_sf_socklist *)sock_kmalloc(sk, |
1886 | IP_SFLSIZE(msf->imsf_numsrc), GFP_KERNEL); | 1897 | IP_SFLSIZE(msf->imsf_numsrc), GFP_KERNEL); |
@@ -1909,8 +1920,11 @@ int ip_mc_msfilter(struct sock *sk, struct ip_msfilter *msf, int ifindex) | |||
1909 | 0, NULL, 0); | 1920 | 0, NULL, 0); |
1910 | pmc->sflist = newpsl; | 1921 | pmc->sflist = newpsl; |
1911 | pmc->sfmode = msf->imsf_fmode; | 1922 | pmc->sfmode = msf->imsf_fmode; |
1923 | err = 0; | ||
1912 | done: | 1924 | done: |
1913 | rtnl_shunlock(); | 1925 | rtnl_shunlock(); |
1926 | if (leavegroup) | ||
1927 | err = ip_mc_leave_group(sk, &imr); | ||
1914 | return err; | 1928 | return err; |
1915 | } | 1929 | } |
1916 | 1930 | ||