diff options
Diffstat (limited to 'net/ipv4/ipmr.c')
-rw-r--r-- | net/ipv4/ipmr.c | 130 |
1 files changed, 112 insertions, 18 deletions
diff --git a/net/ipv4/ipmr.c b/net/ipv4/ipmr.c index a9454cbd953c..5f95b3aa579e 100644 --- a/net/ipv4/ipmr.c +++ b/net/ipv4/ipmr.c | |||
@@ -828,6 +828,49 @@ static struct mfc_cache *ipmr_cache_find(struct mr_table *mrt, | |||
828 | return NULL; | 828 | return NULL; |
829 | } | 829 | } |
830 | 830 | ||
831 | /* Look for a (*,*,oif) entry */ | ||
832 | static struct mfc_cache *ipmr_cache_find_any_parent(struct mr_table *mrt, | ||
833 | int vifi) | ||
834 | { | ||
835 | int line = MFC_HASH(htonl(INADDR_ANY), htonl(INADDR_ANY)); | ||
836 | struct mfc_cache *c; | ||
837 | |||
838 | list_for_each_entry_rcu(c, &mrt->mfc_cache_array[line], list) | ||
839 | if (c->mfc_origin == htonl(INADDR_ANY) && | ||
840 | c->mfc_mcastgrp == htonl(INADDR_ANY) && | ||
841 | c->mfc_un.res.ttls[vifi] < 255) | ||
842 | return c; | ||
843 | |||
844 | return NULL; | ||
845 | } | ||
846 | |||
847 | /* Look for a (*,G) entry */ | ||
848 | static struct mfc_cache *ipmr_cache_find_any(struct mr_table *mrt, | ||
849 | __be32 mcastgrp, int vifi) | ||
850 | { | ||
851 | int line = MFC_HASH(mcastgrp, htonl(INADDR_ANY)); | ||
852 | struct mfc_cache *c, *proxy; | ||
853 | |||
854 | if (mcastgrp == htonl(INADDR_ANY)) | ||
855 | goto skip; | ||
856 | |||
857 | list_for_each_entry_rcu(c, &mrt->mfc_cache_array[line], list) | ||
858 | if (c->mfc_origin == htonl(INADDR_ANY) && | ||
859 | c->mfc_mcastgrp == mcastgrp) { | ||
860 | if (c->mfc_un.res.ttls[vifi] < 255) | ||
861 | return c; | ||
862 | |||
863 | /* It's ok if the vifi is part of the static tree */ | ||
864 | proxy = ipmr_cache_find_any_parent(mrt, | ||
865 | c->mfc_parent); | ||
866 | if (proxy && proxy->mfc_un.res.ttls[vifi] < 255) | ||
867 | return c; | ||
868 | } | ||
869 | |||
870 | skip: | ||
871 | return ipmr_cache_find_any_parent(mrt, vifi); | ||
872 | } | ||
873 | |||
831 | /* | 874 | /* |
832 | * Allocate a multicast cache entry | 875 | * Allocate a multicast cache entry |
833 | */ | 876 | */ |
@@ -1053,7 +1096,7 @@ ipmr_cache_unresolved(struct mr_table *mrt, vifi_t vifi, struct sk_buff *skb) | |||
1053 | * MFC cache manipulation by user space mroute daemon | 1096 | * MFC cache manipulation by user space mroute daemon |
1054 | */ | 1097 | */ |
1055 | 1098 | ||
1056 | static int ipmr_mfc_delete(struct mr_table *mrt, struct mfcctl *mfc) | 1099 | static int ipmr_mfc_delete(struct mr_table *mrt, struct mfcctl *mfc, int parent) |
1057 | { | 1100 | { |
1058 | int line; | 1101 | int line; |
1059 | struct mfc_cache *c, *next; | 1102 | struct mfc_cache *c, *next; |
@@ -1062,7 +1105,8 @@ static int ipmr_mfc_delete(struct mr_table *mrt, struct mfcctl *mfc) | |||
1062 | 1105 | ||
1063 | list_for_each_entry_safe(c, next, &mrt->mfc_cache_array[line], list) { | 1106 | list_for_each_entry_safe(c, next, &mrt->mfc_cache_array[line], list) { |
1064 | if (c->mfc_origin == mfc->mfcc_origin.s_addr && | 1107 | if (c->mfc_origin == mfc->mfcc_origin.s_addr && |
1065 | c->mfc_mcastgrp == mfc->mfcc_mcastgrp.s_addr) { | 1108 | c->mfc_mcastgrp == mfc->mfcc_mcastgrp.s_addr && |
1109 | (parent == -1 || parent == c->mfc_parent)) { | ||
1066 | list_del_rcu(&c->list); | 1110 | list_del_rcu(&c->list); |
1067 | mroute_netlink_event(mrt, c, RTM_DELROUTE); | 1111 | mroute_netlink_event(mrt, c, RTM_DELROUTE); |
1068 | ipmr_cache_free(c); | 1112 | ipmr_cache_free(c); |
@@ -1073,7 +1117,7 @@ static int ipmr_mfc_delete(struct mr_table *mrt, struct mfcctl *mfc) | |||
1073 | } | 1117 | } |
1074 | 1118 | ||
1075 | static int ipmr_mfc_add(struct net *net, struct mr_table *mrt, | 1119 | static int ipmr_mfc_add(struct net *net, struct mr_table *mrt, |
1076 | struct mfcctl *mfc, int mrtsock) | 1120 | struct mfcctl *mfc, int mrtsock, int parent) |
1077 | { | 1121 | { |
1078 | bool found = false; | 1122 | bool found = false; |
1079 | int line; | 1123 | int line; |
@@ -1086,7 +1130,8 @@ static int ipmr_mfc_add(struct net *net, struct mr_table *mrt, | |||
1086 | 1130 | ||
1087 | list_for_each_entry(c, &mrt->mfc_cache_array[line], list) { | 1131 | list_for_each_entry(c, &mrt->mfc_cache_array[line], list) { |
1088 | if (c->mfc_origin == mfc->mfcc_origin.s_addr && | 1132 | if (c->mfc_origin == mfc->mfcc_origin.s_addr && |
1089 | c->mfc_mcastgrp == mfc->mfcc_mcastgrp.s_addr) { | 1133 | c->mfc_mcastgrp == mfc->mfcc_mcastgrp.s_addr && |
1134 | (parent == -1 || parent == c->mfc_parent)) { | ||
1090 | found = true; | 1135 | found = true; |
1091 | break; | 1136 | break; |
1092 | } | 1137 | } |
@@ -1103,7 +1148,8 @@ static int ipmr_mfc_add(struct net *net, struct mr_table *mrt, | |||
1103 | return 0; | 1148 | return 0; |
1104 | } | 1149 | } |
1105 | 1150 | ||
1106 | if (!ipv4_is_multicast(mfc->mfcc_mcastgrp.s_addr)) | 1151 | if (mfc->mfcc_mcastgrp.s_addr != htonl(INADDR_ANY) && |
1152 | !ipv4_is_multicast(mfc->mfcc_mcastgrp.s_addr)) | ||
1107 | return -EINVAL; | 1153 | return -EINVAL; |
1108 | 1154 | ||
1109 | c = ipmr_cache_alloc(); | 1155 | c = ipmr_cache_alloc(); |
@@ -1218,7 +1264,7 @@ static void mrtsock_destruct(struct sock *sk) | |||
1218 | 1264 | ||
1219 | int ip_mroute_setsockopt(struct sock *sk, int optname, char __user *optval, unsigned int optlen) | 1265 | int ip_mroute_setsockopt(struct sock *sk, int optname, char __user *optval, unsigned int optlen) |
1220 | { | 1266 | { |
1221 | int ret; | 1267 | int ret, parent = 0; |
1222 | struct vifctl vif; | 1268 | struct vifctl vif; |
1223 | struct mfcctl mfc; | 1269 | struct mfcctl mfc; |
1224 | struct net *net = sock_net(sk); | 1270 | struct net *net = sock_net(sk); |
@@ -1287,16 +1333,22 @@ int ip_mroute_setsockopt(struct sock *sk, int optname, char __user *optval, unsi | |||
1287 | */ | 1333 | */ |
1288 | case MRT_ADD_MFC: | 1334 | case MRT_ADD_MFC: |
1289 | case MRT_DEL_MFC: | 1335 | case MRT_DEL_MFC: |
1336 | parent = -1; | ||
1337 | case MRT_ADD_MFC_PROXY: | ||
1338 | case MRT_DEL_MFC_PROXY: | ||
1290 | if (optlen != sizeof(mfc)) | 1339 | if (optlen != sizeof(mfc)) |
1291 | return -EINVAL; | 1340 | return -EINVAL; |
1292 | if (copy_from_user(&mfc, optval, sizeof(mfc))) | 1341 | if (copy_from_user(&mfc, optval, sizeof(mfc))) |
1293 | return -EFAULT; | 1342 | return -EFAULT; |
1343 | if (parent == 0) | ||
1344 | parent = mfc.mfcc_parent; | ||
1294 | rtnl_lock(); | 1345 | rtnl_lock(); |
1295 | if (optname == MRT_DEL_MFC) | 1346 | if (optname == MRT_DEL_MFC || optname == MRT_DEL_MFC_PROXY) |
1296 | ret = ipmr_mfc_delete(mrt, &mfc); | 1347 | ret = ipmr_mfc_delete(mrt, &mfc, parent); |
1297 | else | 1348 | else |
1298 | ret = ipmr_mfc_add(net, mrt, &mfc, | 1349 | ret = ipmr_mfc_add(net, mrt, &mfc, |
1299 | sk == rtnl_dereference(mrt->mroute_sk)); | 1350 | sk == rtnl_dereference(mrt->mroute_sk), |
1351 | parent); | ||
1300 | rtnl_unlock(); | 1352 | rtnl_unlock(); |
1301 | return ret; | 1353 | return ret; |
1302 | /* | 1354 | /* |
@@ -1749,17 +1801,28 @@ static int ip_mr_forward(struct net *net, struct mr_table *mrt, | |||
1749 | { | 1801 | { |
1750 | int psend = -1; | 1802 | int psend = -1; |
1751 | int vif, ct; | 1803 | int vif, ct; |
1804 | int true_vifi = ipmr_find_vif(mrt, skb->dev); | ||
1752 | 1805 | ||
1753 | vif = cache->mfc_parent; | 1806 | vif = cache->mfc_parent; |
1754 | cache->mfc_un.res.pkt++; | 1807 | cache->mfc_un.res.pkt++; |
1755 | cache->mfc_un.res.bytes += skb->len; | 1808 | cache->mfc_un.res.bytes += skb->len; |
1756 | 1809 | ||
1810 | if (cache->mfc_origin == htonl(INADDR_ANY) && true_vifi >= 0) { | ||
1811 | struct mfc_cache *cache_proxy; | ||
1812 | |||
1813 | /* For an (*,G) entry, we only check that the incomming | ||
1814 | * interface is part of the static tree. | ||
1815 | */ | ||
1816 | cache_proxy = ipmr_cache_find_any_parent(mrt, vif); | ||
1817 | if (cache_proxy && | ||
1818 | cache_proxy->mfc_un.res.ttls[true_vifi] < 255) | ||
1819 | goto forward; | ||
1820 | } | ||
1821 | |||
1757 | /* | 1822 | /* |
1758 | * Wrong interface: drop packet and (maybe) send PIM assert. | 1823 | * Wrong interface: drop packet and (maybe) send PIM assert. |
1759 | */ | 1824 | */ |
1760 | if (mrt->vif_table[vif].dev != skb->dev) { | 1825 | if (mrt->vif_table[vif].dev != skb->dev) { |
1761 | int true_vifi; | ||
1762 | |||
1763 | if (rt_is_output_route(skb_rtable(skb))) { | 1826 | if (rt_is_output_route(skb_rtable(skb))) { |
1764 | /* It is our own packet, looped back. | 1827 | /* It is our own packet, looped back. |
1765 | * Very complicated situation... | 1828 | * Very complicated situation... |
@@ -1776,7 +1839,6 @@ static int ip_mr_forward(struct net *net, struct mr_table *mrt, | |||
1776 | } | 1839 | } |
1777 | 1840 | ||
1778 | cache->mfc_un.res.wrong_if++; | 1841 | cache->mfc_un.res.wrong_if++; |
1779 | true_vifi = ipmr_find_vif(mrt, skb->dev); | ||
1780 | 1842 | ||
1781 | if (true_vifi >= 0 && mrt->mroute_do_assert && | 1843 | if (true_vifi >= 0 && mrt->mroute_do_assert && |
1782 | /* pimsm uses asserts, when switching from RPT to SPT, | 1844 | /* pimsm uses asserts, when switching from RPT to SPT, |
@@ -1794,15 +1856,34 @@ static int ip_mr_forward(struct net *net, struct mr_table *mrt, | |||
1794 | goto dont_forward; | 1856 | goto dont_forward; |
1795 | } | 1857 | } |
1796 | 1858 | ||
1859 | forward: | ||
1797 | mrt->vif_table[vif].pkt_in++; | 1860 | mrt->vif_table[vif].pkt_in++; |
1798 | mrt->vif_table[vif].bytes_in += skb->len; | 1861 | mrt->vif_table[vif].bytes_in += skb->len; |
1799 | 1862 | ||
1800 | /* | 1863 | /* |
1801 | * Forward the frame | 1864 | * Forward the frame |
1802 | */ | 1865 | */ |
1866 | if (cache->mfc_origin == htonl(INADDR_ANY) && | ||
1867 | cache->mfc_mcastgrp == htonl(INADDR_ANY)) { | ||
1868 | if (true_vifi >= 0 && | ||
1869 | true_vifi != cache->mfc_parent && | ||
1870 | ip_hdr(skb)->ttl > | ||
1871 | cache->mfc_un.res.ttls[cache->mfc_parent]) { | ||
1872 | /* It's an (*,*) entry and the packet is not coming from | ||
1873 | * the upstream: forward the packet to the upstream | ||
1874 | * only. | ||
1875 | */ | ||
1876 | psend = cache->mfc_parent; | ||
1877 | goto last_forward; | ||
1878 | } | ||
1879 | goto dont_forward; | ||
1880 | } | ||
1803 | for (ct = cache->mfc_un.res.maxvif - 1; | 1881 | for (ct = cache->mfc_un.res.maxvif - 1; |
1804 | ct >= cache->mfc_un.res.minvif; ct--) { | 1882 | ct >= cache->mfc_un.res.minvif; ct--) { |
1805 | if (ip_hdr(skb)->ttl > cache->mfc_un.res.ttls[ct]) { | 1883 | /* For (*,G) entry, don't forward to the incoming interface */ |
1884 | if ((cache->mfc_origin != htonl(INADDR_ANY) || | ||
1885 | ct != true_vifi) && | ||
1886 | ip_hdr(skb)->ttl > cache->mfc_un.res.ttls[ct]) { | ||
1806 | if (psend != -1) { | 1887 | if (psend != -1) { |
1807 | struct sk_buff *skb2 = skb_clone(skb, GFP_ATOMIC); | 1888 | struct sk_buff *skb2 = skb_clone(skb, GFP_ATOMIC); |
1808 | 1889 | ||
@@ -1813,6 +1894,7 @@ static int ip_mr_forward(struct net *net, struct mr_table *mrt, | |||
1813 | psend = ct; | 1894 | psend = ct; |
1814 | } | 1895 | } |
1815 | } | 1896 | } |
1897 | last_forward: | ||
1816 | if (psend != -1) { | 1898 | if (psend != -1) { |
1817 | if (local) { | 1899 | if (local) { |
1818 | struct sk_buff *skb2 = skb_clone(skb, GFP_ATOMIC); | 1900 | struct sk_buff *skb2 = skb_clone(skb, GFP_ATOMIC); |
@@ -1902,6 +1984,13 @@ int ip_mr_input(struct sk_buff *skb) | |||
1902 | 1984 | ||
1903 | /* already under rcu_read_lock() */ | 1985 | /* already under rcu_read_lock() */ |
1904 | cache = ipmr_cache_find(mrt, ip_hdr(skb)->saddr, ip_hdr(skb)->daddr); | 1986 | cache = ipmr_cache_find(mrt, ip_hdr(skb)->saddr, ip_hdr(skb)->daddr); |
1987 | if (cache == NULL) { | ||
1988 | int vif = ipmr_find_vif(mrt, skb->dev); | ||
1989 | |||
1990 | if (vif >= 0) | ||
1991 | cache = ipmr_cache_find_any(mrt, ip_hdr(skb)->daddr, | ||
1992 | vif); | ||
1993 | } | ||
1905 | 1994 | ||
1906 | /* | 1995 | /* |
1907 | * No usable cache entry | 1996 | * No usable cache entry |
@@ -2107,7 +2196,12 @@ int ipmr_get_route(struct net *net, struct sk_buff *skb, | |||
2107 | 2196 | ||
2108 | rcu_read_lock(); | 2197 | rcu_read_lock(); |
2109 | cache = ipmr_cache_find(mrt, saddr, daddr); | 2198 | cache = ipmr_cache_find(mrt, saddr, daddr); |
2199 | if (cache == NULL && skb->dev) { | ||
2200 | int vif = ipmr_find_vif(mrt, skb->dev); | ||
2110 | 2201 | ||
2202 | if (vif >= 0) | ||
2203 | cache = ipmr_cache_find_any(mrt, daddr, vif); | ||
2204 | } | ||
2111 | if (cache == NULL) { | 2205 | if (cache == NULL) { |
2112 | struct sk_buff *skb2; | 2206 | struct sk_buff *skb2; |
2113 | struct iphdr *iph; | 2207 | struct iphdr *iph; |
@@ -2609,16 +2703,16 @@ static int __net_init ipmr_net_init(struct net *net) | |||
2609 | 2703 | ||
2610 | #ifdef CONFIG_PROC_FS | 2704 | #ifdef CONFIG_PROC_FS |
2611 | err = -ENOMEM; | 2705 | err = -ENOMEM; |
2612 | if (!proc_net_fops_create(net, "ip_mr_vif", 0, &ipmr_vif_fops)) | 2706 | if (!proc_create("ip_mr_vif", 0, net->proc_net, &ipmr_vif_fops)) |
2613 | goto proc_vif_fail; | 2707 | goto proc_vif_fail; |
2614 | if (!proc_net_fops_create(net, "ip_mr_cache", 0, &ipmr_mfc_fops)) | 2708 | if (!proc_create("ip_mr_cache", 0, net->proc_net, &ipmr_mfc_fops)) |
2615 | goto proc_cache_fail; | 2709 | goto proc_cache_fail; |
2616 | #endif | 2710 | #endif |
2617 | return 0; | 2711 | return 0; |
2618 | 2712 | ||
2619 | #ifdef CONFIG_PROC_FS | 2713 | #ifdef CONFIG_PROC_FS |
2620 | proc_cache_fail: | 2714 | proc_cache_fail: |
2621 | proc_net_remove(net, "ip_mr_vif"); | 2715 | remove_proc_entry("ip_mr_vif", net->proc_net); |
2622 | proc_vif_fail: | 2716 | proc_vif_fail: |
2623 | ipmr_rules_exit(net); | 2717 | ipmr_rules_exit(net); |
2624 | #endif | 2718 | #endif |
@@ -2629,8 +2723,8 @@ fail: | |||
2629 | static void __net_exit ipmr_net_exit(struct net *net) | 2723 | static void __net_exit ipmr_net_exit(struct net *net) |
2630 | { | 2724 | { |
2631 | #ifdef CONFIG_PROC_FS | 2725 | #ifdef CONFIG_PROC_FS |
2632 | proc_net_remove(net, "ip_mr_cache"); | 2726 | remove_proc_entry("ip_mr_cache", net->proc_net); |
2633 | proc_net_remove(net, "ip_mr_vif"); | 2727 | remove_proc_entry("ip_mr_vif", net->proc_net); |
2634 | #endif | 2728 | #endif |
2635 | ipmr_rules_exit(net); | 2729 | ipmr_rules_exit(net); |
2636 | } | 2730 | } |