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 | ||
