diff options
author | Patrick McHardy <kaber@trash.net> | 2007-12-18 01:26:54 -0500 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2008-01-28 17:58:49 -0500 |
commit | d6a2ba07c31b0497fc82a8c175400ea8747da2ef (patch) | |
tree | 57e82c63adafdcf1bd80ed57290624a7d1a4ea5f /net | |
parent | 11f6dff8af95d8d1d14bef70d384029c5acf6e04 (diff) |
[NETFILTER]: arp_tables: add compat support
Signed-off-by: Patrick McHardy <kaber@trash.net>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net')
-rw-r--r-- | net/ipv4/netfilter/arp_tables.c | 748 |
1 files changed, 690 insertions, 58 deletions
diff --git a/net/ipv4/netfilter/arp_tables.c b/net/ipv4/netfilter/arp_tables.c index 029df76b9707..ad2da6db0e26 100644 --- a/net/ipv4/netfilter/arp_tables.c +++ b/net/ipv4/netfilter/arp_tables.c | |||
@@ -19,9 +19,10 @@ | |||
19 | #include <linux/proc_fs.h> | 19 | #include <linux/proc_fs.h> |
20 | #include <linux/module.h> | 20 | #include <linux/module.h> |
21 | #include <linux/init.h> | 21 | #include <linux/init.h> |
22 | |||
23 | #include <asm/uaccess.h> | ||
24 | #include <linux/mutex.h> | 22 | #include <linux/mutex.h> |
23 | #include <linux/err.h> | ||
24 | #include <net/compat.h> | ||
25 | #include <asm/uaccess.h> | ||
25 | 26 | ||
26 | #include <linux/netfilter/x_tables.h> | 27 | #include <linux/netfilter/x_tables.h> |
27 | #include <linux/netfilter_arp/arp_tables.h> | 28 | #include <linux/netfilter_arp/arp_tables.h> |
@@ -782,7 +783,73 @@ static int copy_entries_to_user(unsigned int total_size, | |||
782 | return ret; | 783 | return ret; |
783 | } | 784 | } |
784 | 785 | ||
785 | static int get_info(void __user *user, int *len) | 786 | #ifdef CONFIG_COMPAT |
787 | static void compat_standard_from_user(void *dst, void *src) | ||
788 | { | ||
789 | int v = *(compat_int_t *)src; | ||
790 | |||
791 | if (v > 0) | ||
792 | v += xt_compat_calc_jump(NF_ARP, v); | ||
793 | memcpy(dst, &v, sizeof(v)); | ||
794 | } | ||
795 | |||
796 | static int compat_standard_to_user(void __user *dst, void *src) | ||
797 | { | ||
798 | compat_int_t cv = *(int *)src; | ||
799 | |||
800 | if (cv > 0) | ||
801 | cv -= xt_compat_calc_jump(NF_ARP, cv); | ||
802 | return copy_to_user(dst, &cv, sizeof(cv)) ? -EFAULT : 0; | ||
803 | } | ||
804 | |||
805 | static int compat_calc_entry(struct arpt_entry *e, | ||
806 | const struct xt_table_info *info, | ||
807 | void *base, struct xt_table_info *newinfo) | ||
808 | { | ||
809 | struct arpt_entry_target *t; | ||
810 | unsigned int entry_offset; | ||
811 | int off, i, ret; | ||
812 | |||
813 | off = sizeof(struct arpt_entry) - sizeof(struct compat_arpt_entry); | ||
814 | entry_offset = (void *)e - base; | ||
815 | |||
816 | t = arpt_get_target(e); | ||
817 | off += xt_compat_target_offset(t->u.kernel.target); | ||
818 | newinfo->size -= off; | ||
819 | ret = xt_compat_add_offset(NF_ARP, entry_offset, off); | ||
820 | if (ret) | ||
821 | return ret; | ||
822 | |||
823 | for (i = 0; i < NF_ARP_NUMHOOKS; i++) { | ||
824 | if (info->hook_entry[i] && | ||
825 | (e < (struct arpt_entry *)(base + info->hook_entry[i]))) | ||
826 | newinfo->hook_entry[i] -= off; | ||
827 | if (info->underflow[i] && | ||
828 | (e < (struct arpt_entry *)(base + info->underflow[i]))) | ||
829 | newinfo->underflow[i] -= off; | ||
830 | } | ||
831 | return 0; | ||
832 | } | ||
833 | |||
834 | static int compat_table_info(const struct xt_table_info *info, | ||
835 | struct xt_table_info *newinfo) | ||
836 | { | ||
837 | void *loc_cpu_entry; | ||
838 | |||
839 | if (!newinfo || !info) | ||
840 | return -EINVAL; | ||
841 | |||
842 | /* we dont care about newinfo->entries[] */ | ||
843 | memcpy(newinfo, info, offsetof(struct xt_table_info, entries)); | ||
844 | newinfo->initial_entries = 0; | ||
845 | loc_cpu_entry = info->entries[raw_smp_processor_id()]; | ||
846 | return ARPT_ENTRY_ITERATE(loc_cpu_entry, info->size, | ||
847 | compat_calc_entry, info, loc_cpu_entry, | ||
848 | newinfo); | ||
849 | } | ||
850 | #endif | ||
851 | |||
852 | static int get_info(void __user *user, int *len, int compat) | ||
786 | { | 853 | { |
787 | char name[ARPT_TABLE_MAXNAMELEN]; | 854 | char name[ARPT_TABLE_MAXNAMELEN]; |
788 | struct arpt_table *t; | 855 | struct arpt_table *t; |
@@ -798,13 +865,24 @@ static int get_info(void __user *user, int *len) | |||
798 | return -EFAULT; | 865 | return -EFAULT; |
799 | 866 | ||
800 | name[ARPT_TABLE_MAXNAMELEN-1] = '\0'; | 867 | name[ARPT_TABLE_MAXNAMELEN-1] = '\0'; |
801 | 868 | #ifdef CONFIG_COMPAT | |
869 | if (compat) | ||
870 | xt_compat_lock(NF_ARP); | ||
871 | #endif | ||
802 | t = try_then_request_module(xt_find_table_lock(NF_ARP, name), | 872 | t = try_then_request_module(xt_find_table_lock(NF_ARP, name), |
803 | "arptable_%s", name); | 873 | "arptable_%s", name); |
804 | if (t && !IS_ERR(t)) { | 874 | if (t && !IS_ERR(t)) { |
805 | struct arpt_getinfo info; | 875 | struct arpt_getinfo info; |
806 | struct xt_table_info *private = t->private; | 876 | struct xt_table_info *private = t->private; |
807 | 877 | ||
878 | #ifdef CONFIG_COMPAT | ||
879 | if (compat) { | ||
880 | struct xt_table_info tmp; | ||
881 | ret = compat_table_info(private, &tmp); | ||
882 | xt_compat_flush_offsets(NF_ARP); | ||
883 | private = &tmp; | ||
884 | } | ||
885 | #endif | ||
808 | info.valid_hooks = t->valid_hooks; | 886 | info.valid_hooks = t->valid_hooks; |
809 | memcpy(info.hook_entry, private->hook_entry, | 887 | memcpy(info.hook_entry, private->hook_entry, |
810 | sizeof(info.hook_entry)); | 888 | sizeof(info.hook_entry)); |
@@ -822,6 +900,10 @@ static int get_info(void __user *user, int *len) | |||
822 | module_put(t->me); | 900 | module_put(t->me); |
823 | } else | 901 | } else |
824 | ret = t ? PTR_ERR(t) : -ENOENT; | 902 | ret = t ? PTR_ERR(t) : -ENOENT; |
903 | #ifdef CONFIG_COMPAT | ||
904 | if (compat) | ||
905 | xt_compat_unlock(NF_ARP); | ||
906 | #endif | ||
825 | return ret; | 907 | return ret; |
826 | } | 908 | } |
827 | 909 | ||
@@ -864,65 +946,41 @@ static int get_entries(struct arpt_get_entries __user *uptr, int *len) | |||
864 | return ret; | 946 | return ret; |
865 | } | 947 | } |
866 | 948 | ||
867 | static int do_replace(void __user *user, unsigned int len) | 949 | static int __do_replace(const char *name, unsigned int valid_hooks, |
950 | struct xt_table_info *newinfo, | ||
951 | unsigned int num_counters, | ||
952 | void __user *counters_ptr) | ||
868 | { | 953 | { |
869 | int ret; | 954 | int ret; |
870 | struct arpt_replace tmp; | ||
871 | struct arpt_table *t; | 955 | struct arpt_table *t; |
872 | struct xt_table_info *newinfo, *oldinfo; | 956 | struct xt_table_info *oldinfo; |
873 | struct xt_counters *counters; | 957 | struct xt_counters *counters; |
874 | void *loc_cpu_entry, *loc_cpu_old_entry; | 958 | void *loc_cpu_old_entry; |
875 | |||
876 | if (copy_from_user(&tmp, user, sizeof(tmp)) != 0) | ||
877 | return -EFAULT; | ||
878 | |||
879 | /* overflow check */ | ||
880 | if (tmp.num_counters >= INT_MAX / sizeof(struct xt_counters)) | ||
881 | return -ENOMEM; | ||
882 | |||
883 | newinfo = xt_alloc_table_info(tmp.size); | ||
884 | if (!newinfo) | ||
885 | return -ENOMEM; | ||
886 | 959 | ||
887 | /* choose the copy that is on our node/cpu */ | 960 | ret = 0; |
888 | loc_cpu_entry = newinfo->entries[raw_smp_processor_id()]; | 961 | counters = vmalloc_node(num_counters * sizeof(struct xt_counters), |
889 | if (copy_from_user(loc_cpu_entry, user + sizeof(tmp), | ||
890 | tmp.size) != 0) { | ||
891 | ret = -EFAULT; | ||
892 | goto free_newinfo; | ||
893 | } | ||
894 | |||
895 | counters = vmalloc_node(tmp.num_counters * sizeof(struct xt_counters), | ||
896 | numa_node_id()); | 962 | numa_node_id()); |
897 | if (!counters) { | 963 | if (!counters) { |
898 | ret = -ENOMEM; | 964 | ret = -ENOMEM; |
899 | goto free_newinfo; | 965 | goto out; |
900 | } | 966 | } |
901 | 967 | ||
902 | ret = translate_table(tmp.name, tmp.valid_hooks, | 968 | t = try_then_request_module(xt_find_table_lock(NF_ARP, name), |
903 | newinfo, loc_cpu_entry, tmp.size, tmp.num_entries, | 969 | "arptable_%s", name); |
904 | tmp.hook_entry, tmp.underflow); | ||
905 | if (ret != 0) | ||
906 | goto free_newinfo_counters; | ||
907 | |||
908 | duprintf("arp_tables: Translated table\n"); | ||
909 | |||
910 | t = try_then_request_module(xt_find_table_lock(NF_ARP, tmp.name), | ||
911 | "arptable_%s", tmp.name); | ||
912 | if (!t || IS_ERR(t)) { | 970 | if (!t || IS_ERR(t)) { |
913 | ret = t ? PTR_ERR(t) : -ENOENT; | 971 | ret = t ? PTR_ERR(t) : -ENOENT; |
914 | goto free_newinfo_counters_untrans; | 972 | goto free_newinfo_counters_untrans; |
915 | } | 973 | } |
916 | 974 | ||
917 | /* You lied! */ | 975 | /* You lied! */ |
918 | if (tmp.valid_hooks != t->valid_hooks) { | 976 | if (valid_hooks != t->valid_hooks) { |
919 | duprintf("Valid hook crap: %08X vs %08X\n", | 977 | duprintf("Valid hook crap: %08X vs %08X\n", |
920 | tmp.valid_hooks, t->valid_hooks); | 978 | valid_hooks, t->valid_hooks); |
921 | ret = -EINVAL; | 979 | ret = -EINVAL; |
922 | goto put_module; | 980 | goto put_module; |
923 | } | 981 | } |
924 | 982 | ||
925 | oldinfo = xt_replace_table(t, tmp.num_counters, newinfo, &ret); | 983 | oldinfo = xt_replace_table(t, num_counters, newinfo, &ret); |
926 | if (!oldinfo) | 984 | if (!oldinfo) |
927 | goto put_module; | 985 | goto put_module; |
928 | 986 | ||
@@ -940,11 +998,12 @@ static int do_replace(void __user *user, unsigned int len) | |||
940 | get_counters(oldinfo, counters); | 998 | get_counters(oldinfo, counters); |
941 | /* Decrease module usage counts and free resource */ | 999 | /* Decrease module usage counts and free resource */ |
942 | loc_cpu_old_entry = oldinfo->entries[raw_smp_processor_id()]; | 1000 | loc_cpu_old_entry = oldinfo->entries[raw_smp_processor_id()]; |
943 | ARPT_ENTRY_ITERATE(loc_cpu_old_entry, oldinfo->size, cleanup_entry,NULL); | 1001 | ARPT_ENTRY_ITERATE(loc_cpu_old_entry, oldinfo->size, cleanup_entry, |
1002 | NULL); | ||
944 | 1003 | ||
945 | xt_free_table_info(oldinfo); | 1004 | xt_free_table_info(oldinfo); |
946 | if (copy_to_user(tmp.counters, counters, | 1005 | if (copy_to_user(counters_ptr, counters, |
947 | sizeof(struct xt_counters) * tmp.num_counters) != 0) | 1006 | sizeof(struct xt_counters) * num_counters) != 0) |
948 | ret = -EFAULT; | 1007 | ret = -EFAULT; |
949 | vfree(counters); | 1008 | vfree(counters); |
950 | xt_table_unlock(t); | 1009 | xt_table_unlock(t); |
@@ -954,9 +1013,53 @@ static int do_replace(void __user *user, unsigned int len) | |||
954 | module_put(t->me); | 1013 | module_put(t->me); |
955 | xt_table_unlock(t); | 1014 | xt_table_unlock(t); |
956 | free_newinfo_counters_untrans: | 1015 | free_newinfo_counters_untrans: |
957 | ARPT_ENTRY_ITERATE(loc_cpu_entry, newinfo->size, cleanup_entry, NULL); | ||
958 | free_newinfo_counters: | ||
959 | vfree(counters); | 1016 | vfree(counters); |
1017 | out: | ||
1018 | return ret; | ||
1019 | } | ||
1020 | |||
1021 | static int do_replace(void __user *user, unsigned int len) | ||
1022 | { | ||
1023 | int ret; | ||
1024 | struct arpt_replace tmp; | ||
1025 | struct xt_table_info *newinfo; | ||
1026 | void *loc_cpu_entry; | ||
1027 | |||
1028 | if (copy_from_user(&tmp, user, sizeof(tmp)) != 0) | ||
1029 | return -EFAULT; | ||
1030 | |||
1031 | /* overflow check */ | ||
1032 | if (tmp.num_counters >= INT_MAX / sizeof(struct xt_counters)) | ||
1033 | return -ENOMEM; | ||
1034 | |||
1035 | newinfo = xt_alloc_table_info(tmp.size); | ||
1036 | if (!newinfo) | ||
1037 | return -ENOMEM; | ||
1038 | |||
1039 | /* choose the copy that is on our node/cpu */ | ||
1040 | loc_cpu_entry = newinfo->entries[raw_smp_processor_id()]; | ||
1041 | if (copy_from_user(loc_cpu_entry, user + sizeof(tmp), | ||
1042 | tmp.size) != 0) { | ||
1043 | ret = -EFAULT; | ||
1044 | goto free_newinfo; | ||
1045 | } | ||
1046 | |||
1047 | ret = translate_table(tmp.name, tmp.valid_hooks, | ||
1048 | newinfo, loc_cpu_entry, tmp.size, tmp.num_entries, | ||
1049 | tmp.hook_entry, tmp.underflow); | ||
1050 | if (ret != 0) | ||
1051 | goto free_newinfo; | ||
1052 | |||
1053 | duprintf("arp_tables: Translated table\n"); | ||
1054 | |||
1055 | ret = __do_replace(tmp.name, tmp.valid_hooks, newinfo, | ||
1056 | tmp.num_counters, tmp.counters); | ||
1057 | if (ret) | ||
1058 | goto free_newinfo_untrans; | ||
1059 | return 0; | ||
1060 | |||
1061 | free_newinfo_untrans: | ||
1062 | ARPT_ENTRY_ITERATE(loc_cpu_entry, newinfo->size, cleanup_entry, NULL); | ||
960 | free_newinfo: | 1063 | free_newinfo: |
961 | xt_free_table_info(newinfo); | 1064 | xt_free_table_info(newinfo); |
962 | return ret; | 1065 | return ret; |
@@ -976,31 +1079,59 @@ static inline int add_counter_to_entry(struct arpt_entry *e, | |||
976 | return 0; | 1079 | return 0; |
977 | } | 1080 | } |
978 | 1081 | ||
979 | static int do_add_counters(void __user *user, unsigned int len) | 1082 | static int do_add_counters(void __user *user, unsigned int len, int compat) |
980 | { | 1083 | { |
981 | unsigned int i; | 1084 | unsigned int i; |
982 | struct xt_counters_info tmp, *paddc; | 1085 | struct xt_counters_info tmp; |
1086 | struct xt_counters *paddc; | ||
1087 | unsigned int num_counters; | ||
1088 | char *name; | ||
1089 | int size; | ||
1090 | void *ptmp; | ||
983 | struct arpt_table *t; | 1091 | struct arpt_table *t; |
984 | struct xt_table_info *private; | 1092 | struct xt_table_info *private; |
985 | int ret = 0; | 1093 | int ret = 0; |
986 | void *loc_cpu_entry; | 1094 | void *loc_cpu_entry; |
1095 | #ifdef CONFIG_COMPAT | ||
1096 | struct compat_xt_counters_info compat_tmp; | ||
987 | 1097 | ||
988 | if (copy_from_user(&tmp, user, sizeof(tmp)) != 0) | 1098 | if (compat) { |
1099 | ptmp = &compat_tmp; | ||
1100 | size = sizeof(struct compat_xt_counters_info); | ||
1101 | } else | ||
1102 | #endif | ||
1103 | { | ||
1104 | ptmp = &tmp; | ||
1105 | size = sizeof(struct xt_counters_info); | ||
1106 | } | ||
1107 | |||
1108 | if (copy_from_user(ptmp, user, size) != 0) | ||
989 | return -EFAULT; | 1109 | return -EFAULT; |
990 | 1110 | ||
991 | if (len != sizeof(tmp) + tmp.num_counters*sizeof(struct xt_counters)) | 1111 | #ifdef CONFIG_COMPAT |
1112 | if (compat) { | ||
1113 | num_counters = compat_tmp.num_counters; | ||
1114 | name = compat_tmp.name; | ||
1115 | } else | ||
1116 | #endif | ||
1117 | { | ||
1118 | num_counters = tmp.num_counters; | ||
1119 | name = tmp.name; | ||
1120 | } | ||
1121 | |||
1122 | if (len != size + num_counters * sizeof(struct xt_counters)) | ||
992 | return -EINVAL; | 1123 | return -EINVAL; |
993 | 1124 | ||
994 | paddc = vmalloc_node(len, numa_node_id()); | 1125 | paddc = vmalloc_node(len - size, numa_node_id()); |
995 | if (!paddc) | 1126 | if (!paddc) |
996 | return -ENOMEM; | 1127 | return -ENOMEM; |
997 | 1128 | ||
998 | if (copy_from_user(paddc, user, len) != 0) { | 1129 | if (copy_from_user(paddc, user + size, len - size) != 0) { |
999 | ret = -EFAULT; | 1130 | ret = -EFAULT; |
1000 | goto free; | 1131 | goto free; |
1001 | } | 1132 | } |
1002 | 1133 | ||
1003 | t = xt_find_table_lock(NF_ARP, tmp.name); | 1134 | t = xt_find_table_lock(NF_ARP, name); |
1004 | if (!t || IS_ERR(t)) { | 1135 | if (!t || IS_ERR(t)) { |
1005 | ret = t ? PTR_ERR(t) : -ENOENT; | 1136 | ret = t ? PTR_ERR(t) : -ENOENT; |
1006 | goto free; | 1137 | goto free; |
@@ -1008,7 +1139,7 @@ static int do_add_counters(void __user *user, unsigned int len) | |||
1008 | 1139 | ||
1009 | write_lock_bh(&t->lock); | 1140 | write_lock_bh(&t->lock); |
1010 | private = t->private; | 1141 | private = t->private; |
1011 | if (private->number != tmp.num_counters) { | 1142 | if (private->number != num_counters) { |
1012 | ret = -EINVAL; | 1143 | ret = -EINVAL; |
1013 | goto unlock_up_free; | 1144 | goto unlock_up_free; |
1014 | } | 1145 | } |
@@ -1019,7 +1150,7 @@ static int do_add_counters(void __user *user, unsigned int len) | |||
1019 | ARPT_ENTRY_ITERATE(loc_cpu_entry, | 1150 | ARPT_ENTRY_ITERATE(loc_cpu_entry, |
1020 | private->size, | 1151 | private->size, |
1021 | add_counter_to_entry, | 1152 | add_counter_to_entry, |
1022 | paddc->counters, | 1153 | paddc, |
1023 | &i); | 1154 | &i); |
1024 | unlock_up_free: | 1155 | unlock_up_free: |
1025 | write_unlock_bh(&t->lock); | 1156 | write_unlock_bh(&t->lock); |
@@ -1031,6 +1162,496 @@ static int do_add_counters(void __user *user, unsigned int len) | |||
1031 | return ret; | 1162 | return ret; |
1032 | } | 1163 | } |
1033 | 1164 | ||
1165 | #ifdef CONFIG_COMPAT | ||
1166 | static inline int | ||
1167 | compat_release_entry(struct compat_arpt_entry *e, unsigned int *i) | ||
1168 | { | ||
1169 | struct arpt_entry_target *t; | ||
1170 | |||
1171 | if (i && (*i)-- == 0) | ||
1172 | return 1; | ||
1173 | |||
1174 | t = compat_arpt_get_target(e); | ||
1175 | module_put(t->u.kernel.target->me); | ||
1176 | return 0; | ||
1177 | } | ||
1178 | |||
1179 | static inline int | ||
1180 | check_compat_entry_size_and_hooks(struct compat_arpt_entry *e, | ||
1181 | struct xt_table_info *newinfo, | ||
1182 | unsigned int *size, | ||
1183 | unsigned char *base, | ||
1184 | unsigned char *limit, | ||
1185 | unsigned int *hook_entries, | ||
1186 | unsigned int *underflows, | ||
1187 | unsigned int *i, | ||
1188 | const char *name) | ||
1189 | { | ||
1190 | struct arpt_entry_target *t; | ||
1191 | struct xt_target *target; | ||
1192 | unsigned int entry_offset; | ||
1193 | int ret, off, h; | ||
1194 | |||
1195 | duprintf("check_compat_entry_size_and_hooks %p\n", e); | ||
1196 | if ((unsigned long)e % __alignof__(struct compat_arpt_entry) != 0 | ||
1197 | || (unsigned char *)e + sizeof(struct compat_arpt_entry) >= limit) { | ||
1198 | duprintf("Bad offset %p, limit = %p\n", e, limit); | ||
1199 | return -EINVAL; | ||
1200 | } | ||
1201 | |||
1202 | if (e->next_offset < sizeof(struct compat_arpt_entry) + | ||
1203 | sizeof(struct compat_xt_entry_target)) { | ||
1204 | duprintf("checking: element %p size %u\n", | ||
1205 | e, e->next_offset); | ||
1206 | return -EINVAL; | ||
1207 | } | ||
1208 | |||
1209 | /* For purposes of check_entry casting the compat entry is fine */ | ||
1210 | ret = check_entry((struct arpt_entry *)e, name); | ||
1211 | if (ret) | ||
1212 | return ret; | ||
1213 | |||
1214 | off = sizeof(struct arpt_entry) - sizeof(struct compat_arpt_entry); | ||
1215 | entry_offset = (void *)e - (void *)base; | ||
1216 | |||
1217 | t = compat_arpt_get_target(e); | ||
1218 | target = try_then_request_module(xt_find_target(NF_ARP, | ||
1219 | t->u.user.name, | ||
1220 | t->u.user.revision), | ||
1221 | "arpt_%s", t->u.user.name); | ||
1222 | if (IS_ERR(target) || !target) { | ||
1223 | duprintf("check_compat_entry_size_and_hooks: `%s' not found\n", | ||
1224 | t->u.user.name); | ||
1225 | ret = target ? PTR_ERR(target) : -ENOENT; | ||
1226 | goto out; | ||
1227 | } | ||
1228 | t->u.kernel.target = target; | ||
1229 | |||
1230 | off += xt_compat_target_offset(target); | ||
1231 | *size += off; | ||
1232 | ret = xt_compat_add_offset(NF_ARP, entry_offset, off); | ||
1233 | if (ret) | ||
1234 | goto release_target; | ||
1235 | |||
1236 | /* Check hooks & underflows */ | ||
1237 | for (h = 0; h < NF_ARP_NUMHOOKS; h++) { | ||
1238 | if ((unsigned char *)e - base == hook_entries[h]) | ||
1239 | newinfo->hook_entry[h] = hook_entries[h]; | ||
1240 | if ((unsigned char *)e - base == underflows[h]) | ||
1241 | newinfo->underflow[h] = underflows[h]; | ||
1242 | } | ||
1243 | |||
1244 | /* Clear counters and comefrom */ | ||
1245 | memset(&e->counters, 0, sizeof(e->counters)); | ||
1246 | e->comefrom = 0; | ||
1247 | |||
1248 | (*i)++; | ||
1249 | return 0; | ||
1250 | |||
1251 | release_target: | ||
1252 | module_put(t->u.kernel.target->me); | ||
1253 | out: | ||
1254 | return ret; | ||
1255 | } | ||
1256 | |||
1257 | static int | ||
1258 | compat_copy_entry_from_user(struct compat_arpt_entry *e, void **dstptr, | ||
1259 | unsigned int *size, const char *name, | ||
1260 | struct xt_table_info *newinfo, unsigned char *base) | ||
1261 | { | ||
1262 | struct arpt_entry_target *t; | ||
1263 | struct xt_target *target; | ||
1264 | struct arpt_entry *de; | ||
1265 | unsigned int origsize; | ||
1266 | int ret, h; | ||
1267 | |||
1268 | ret = 0; | ||
1269 | origsize = *size; | ||
1270 | de = (struct arpt_entry *)*dstptr; | ||
1271 | memcpy(de, e, sizeof(struct arpt_entry)); | ||
1272 | memcpy(&de->counters, &e->counters, sizeof(e->counters)); | ||
1273 | |||
1274 | *dstptr += sizeof(struct arpt_entry); | ||
1275 | *size += sizeof(struct arpt_entry) - sizeof(struct compat_arpt_entry); | ||
1276 | |||
1277 | de->target_offset = e->target_offset - (origsize - *size); | ||
1278 | t = compat_arpt_get_target(e); | ||
1279 | target = t->u.kernel.target; | ||
1280 | xt_compat_target_from_user(t, dstptr, size); | ||
1281 | |||
1282 | de->next_offset = e->next_offset - (origsize - *size); | ||
1283 | for (h = 0; h < NF_ARP_NUMHOOKS; h++) { | ||
1284 | if ((unsigned char *)de - base < newinfo->hook_entry[h]) | ||
1285 | newinfo->hook_entry[h] -= origsize - *size; | ||
1286 | if ((unsigned char *)de - base < newinfo->underflow[h]) | ||
1287 | newinfo->underflow[h] -= origsize - *size; | ||
1288 | } | ||
1289 | return ret; | ||
1290 | } | ||
1291 | |||
1292 | static inline int compat_check_entry(struct arpt_entry *e, const char *name, | ||
1293 | unsigned int *i) | ||
1294 | { | ||
1295 | int ret; | ||
1296 | |||
1297 | ret = check_target(e, name); | ||
1298 | if (ret) | ||
1299 | return ret; | ||
1300 | |||
1301 | (*i)++; | ||
1302 | return 0; | ||
1303 | } | ||
1304 | |||
1305 | static int translate_compat_table(const char *name, | ||
1306 | unsigned int valid_hooks, | ||
1307 | struct xt_table_info **pinfo, | ||
1308 | void **pentry0, | ||
1309 | unsigned int total_size, | ||
1310 | unsigned int number, | ||
1311 | unsigned int *hook_entries, | ||
1312 | unsigned int *underflows) | ||
1313 | { | ||
1314 | unsigned int i, j; | ||
1315 | struct xt_table_info *newinfo, *info; | ||
1316 | void *pos, *entry0, *entry1; | ||
1317 | unsigned int size; | ||
1318 | int ret; | ||
1319 | |||
1320 | info = *pinfo; | ||
1321 | entry0 = *pentry0; | ||
1322 | size = total_size; | ||
1323 | info->number = number; | ||
1324 | |||
1325 | /* Init all hooks to impossible value. */ | ||
1326 | for (i = 0; i < NF_ARP_NUMHOOKS; i++) { | ||
1327 | info->hook_entry[i] = 0xFFFFFFFF; | ||
1328 | info->underflow[i] = 0xFFFFFFFF; | ||
1329 | } | ||
1330 | |||
1331 | duprintf("translate_compat_table: size %u\n", info->size); | ||
1332 | j = 0; | ||
1333 | xt_compat_lock(NF_ARP); | ||
1334 | /* Walk through entries, checking offsets. */ | ||
1335 | ret = COMPAT_ARPT_ENTRY_ITERATE(entry0, total_size, | ||
1336 | check_compat_entry_size_and_hooks, | ||
1337 | info, &size, entry0, | ||
1338 | entry0 + total_size, | ||
1339 | hook_entries, underflows, &j, name); | ||
1340 | if (ret != 0) | ||
1341 | goto out_unlock; | ||
1342 | |||
1343 | ret = -EINVAL; | ||
1344 | if (j != number) { | ||
1345 | duprintf("translate_compat_table: %u not %u entries\n", | ||
1346 | j, number); | ||
1347 | goto out_unlock; | ||
1348 | } | ||
1349 | |||
1350 | /* Check hooks all assigned */ | ||
1351 | for (i = 0; i < NF_ARP_NUMHOOKS; i++) { | ||
1352 | /* Only hooks which are valid */ | ||
1353 | if (!(valid_hooks & (1 << i))) | ||
1354 | continue; | ||
1355 | if (info->hook_entry[i] == 0xFFFFFFFF) { | ||
1356 | duprintf("Invalid hook entry %u %u\n", | ||
1357 | i, hook_entries[i]); | ||
1358 | goto out_unlock; | ||
1359 | } | ||
1360 | if (info->underflow[i] == 0xFFFFFFFF) { | ||
1361 | duprintf("Invalid underflow %u %u\n", | ||
1362 | i, underflows[i]); | ||
1363 | goto out_unlock; | ||
1364 | } | ||
1365 | } | ||
1366 | |||
1367 | ret = -ENOMEM; | ||
1368 | newinfo = xt_alloc_table_info(size); | ||
1369 | if (!newinfo) | ||
1370 | goto out_unlock; | ||
1371 | |||
1372 | newinfo->number = number; | ||
1373 | for (i = 0; i < NF_ARP_NUMHOOKS; i++) { | ||
1374 | newinfo->hook_entry[i] = info->hook_entry[i]; | ||
1375 | newinfo->underflow[i] = info->underflow[i]; | ||
1376 | } | ||
1377 | entry1 = newinfo->entries[raw_smp_processor_id()]; | ||
1378 | pos = entry1; | ||
1379 | size = total_size; | ||
1380 | ret = COMPAT_ARPT_ENTRY_ITERATE(entry0, total_size, | ||
1381 | compat_copy_entry_from_user, | ||
1382 | &pos, &size, name, newinfo, entry1); | ||
1383 | xt_compat_flush_offsets(NF_ARP); | ||
1384 | xt_compat_unlock(NF_ARP); | ||
1385 | if (ret) | ||
1386 | goto free_newinfo; | ||
1387 | |||
1388 | ret = -ELOOP; | ||
1389 | if (!mark_source_chains(newinfo, valid_hooks, entry1)) | ||
1390 | goto free_newinfo; | ||
1391 | |||
1392 | i = 0; | ||
1393 | ret = ARPT_ENTRY_ITERATE(entry1, newinfo->size, compat_check_entry, | ||
1394 | name, &i); | ||
1395 | if (ret) { | ||
1396 | j -= i; | ||
1397 | COMPAT_ARPT_ENTRY_ITERATE_CONTINUE(entry0, newinfo->size, i, | ||
1398 | compat_release_entry, &j); | ||
1399 | ARPT_ENTRY_ITERATE(entry1, newinfo->size, cleanup_entry, &i); | ||
1400 | xt_free_table_info(newinfo); | ||
1401 | return ret; | ||
1402 | } | ||
1403 | |||
1404 | /* And one copy for every other CPU */ | ||
1405 | for_each_possible_cpu(i) | ||
1406 | if (newinfo->entries[i] && newinfo->entries[i] != entry1) | ||
1407 | memcpy(newinfo->entries[i], entry1, newinfo->size); | ||
1408 | |||
1409 | *pinfo = newinfo; | ||
1410 | *pentry0 = entry1; | ||
1411 | xt_free_table_info(info); | ||
1412 | return 0; | ||
1413 | |||
1414 | free_newinfo: | ||
1415 | xt_free_table_info(newinfo); | ||
1416 | out: | ||
1417 | COMPAT_ARPT_ENTRY_ITERATE(entry0, total_size, compat_release_entry, &j); | ||
1418 | return ret; | ||
1419 | out_unlock: | ||
1420 | xt_compat_flush_offsets(NF_ARP); | ||
1421 | xt_compat_unlock(NF_ARP); | ||
1422 | goto out; | ||
1423 | } | ||
1424 | |||
1425 | struct compat_arpt_replace { | ||
1426 | char name[ARPT_TABLE_MAXNAMELEN]; | ||
1427 | u32 valid_hooks; | ||
1428 | u32 num_entries; | ||
1429 | u32 size; | ||
1430 | u32 hook_entry[NF_ARP_NUMHOOKS]; | ||
1431 | u32 underflow[NF_ARP_NUMHOOKS]; | ||
1432 | u32 num_counters; | ||
1433 | compat_uptr_t counters; | ||
1434 | struct compat_arpt_entry entries[0]; | ||
1435 | }; | ||
1436 | |||
1437 | static int compat_do_replace(void __user *user, unsigned int len) | ||
1438 | { | ||
1439 | int ret; | ||
1440 | struct compat_arpt_replace tmp; | ||
1441 | struct xt_table_info *newinfo; | ||
1442 | void *loc_cpu_entry; | ||
1443 | |||
1444 | if (copy_from_user(&tmp, user, sizeof(tmp)) != 0) | ||
1445 | return -EFAULT; | ||
1446 | |||
1447 | /* overflow check */ | ||
1448 | if (tmp.size >= INT_MAX / num_possible_cpus()) | ||
1449 | return -ENOMEM; | ||
1450 | if (tmp.num_counters >= INT_MAX / sizeof(struct xt_counters)) | ||
1451 | return -ENOMEM; | ||
1452 | |||
1453 | newinfo = xt_alloc_table_info(tmp.size); | ||
1454 | if (!newinfo) | ||
1455 | return -ENOMEM; | ||
1456 | |||
1457 | /* choose the copy that is on our node/cpu */ | ||
1458 | loc_cpu_entry = newinfo->entries[raw_smp_processor_id()]; | ||
1459 | if (copy_from_user(loc_cpu_entry, user + sizeof(tmp), tmp.size) != 0) { | ||
1460 | ret = -EFAULT; | ||
1461 | goto free_newinfo; | ||
1462 | } | ||
1463 | |||
1464 | ret = translate_compat_table(tmp.name, tmp.valid_hooks, | ||
1465 | &newinfo, &loc_cpu_entry, tmp.size, | ||
1466 | tmp.num_entries, tmp.hook_entry, | ||
1467 | tmp.underflow); | ||
1468 | if (ret != 0) | ||
1469 | goto free_newinfo; | ||
1470 | |||
1471 | duprintf("compat_do_replace: Translated table\n"); | ||
1472 | |||
1473 | ret = __do_replace(tmp.name, tmp.valid_hooks, newinfo, | ||
1474 | tmp.num_counters, compat_ptr(tmp.counters)); | ||
1475 | if (ret) | ||
1476 | goto free_newinfo_untrans; | ||
1477 | return 0; | ||
1478 | |||
1479 | free_newinfo_untrans: | ||
1480 | ARPT_ENTRY_ITERATE(loc_cpu_entry, newinfo->size, cleanup_entry, NULL); | ||
1481 | free_newinfo: | ||
1482 | xt_free_table_info(newinfo); | ||
1483 | return ret; | ||
1484 | } | ||
1485 | |||
1486 | static int compat_do_arpt_set_ctl(struct sock *sk, int cmd, void __user *user, | ||
1487 | unsigned int len) | ||
1488 | { | ||
1489 | int ret; | ||
1490 | |||
1491 | if (!capable(CAP_NET_ADMIN)) | ||
1492 | return -EPERM; | ||
1493 | |||
1494 | switch (cmd) { | ||
1495 | case ARPT_SO_SET_REPLACE: | ||
1496 | ret = compat_do_replace(user, len); | ||
1497 | break; | ||
1498 | |||
1499 | case ARPT_SO_SET_ADD_COUNTERS: | ||
1500 | ret = do_add_counters(user, len, 1); | ||
1501 | break; | ||
1502 | |||
1503 | default: | ||
1504 | duprintf("do_arpt_set_ctl: unknown request %i\n", cmd); | ||
1505 | ret = -EINVAL; | ||
1506 | } | ||
1507 | |||
1508 | return ret; | ||
1509 | } | ||
1510 | |||
1511 | static int compat_copy_entry_to_user(struct arpt_entry *e, void __user **dstptr, | ||
1512 | compat_uint_t *size, | ||
1513 | struct xt_counters *counters, | ||
1514 | unsigned int *i) | ||
1515 | { | ||
1516 | struct arpt_entry_target *t; | ||
1517 | struct compat_arpt_entry __user *ce; | ||
1518 | u_int16_t target_offset, next_offset; | ||
1519 | compat_uint_t origsize; | ||
1520 | int ret; | ||
1521 | |||
1522 | ret = -EFAULT; | ||
1523 | origsize = *size; | ||
1524 | ce = (struct compat_arpt_entry __user *)*dstptr; | ||
1525 | if (copy_to_user(ce, e, sizeof(struct arpt_entry))) | ||
1526 | goto out; | ||
1527 | |||
1528 | if (copy_to_user(&ce->counters, &counters[*i], sizeof(counters[*i]))) | ||
1529 | goto out; | ||
1530 | |||
1531 | *dstptr += sizeof(struct compat_arpt_entry); | ||
1532 | *size -= sizeof(struct arpt_entry) - sizeof(struct compat_arpt_entry); | ||
1533 | |||
1534 | target_offset = e->target_offset - (origsize - *size); | ||
1535 | |||
1536 | t = arpt_get_target(e); | ||
1537 | ret = xt_compat_target_to_user(t, dstptr, size); | ||
1538 | if (ret) | ||
1539 | goto out; | ||
1540 | ret = -EFAULT; | ||
1541 | next_offset = e->next_offset - (origsize - *size); | ||
1542 | if (put_user(target_offset, &ce->target_offset)) | ||
1543 | goto out; | ||
1544 | if (put_user(next_offset, &ce->next_offset)) | ||
1545 | goto out; | ||
1546 | |||
1547 | (*i)++; | ||
1548 | return 0; | ||
1549 | out: | ||
1550 | return ret; | ||
1551 | } | ||
1552 | |||
1553 | static int compat_copy_entries_to_user(unsigned int total_size, | ||
1554 | struct arpt_table *table, | ||
1555 | void __user *userptr) | ||
1556 | { | ||
1557 | struct xt_counters *counters; | ||
1558 | struct xt_table_info *private = table->private; | ||
1559 | void __user *pos; | ||
1560 | unsigned int size; | ||
1561 | int ret = 0; | ||
1562 | void *loc_cpu_entry; | ||
1563 | unsigned int i = 0; | ||
1564 | |||
1565 | counters = alloc_counters(table); | ||
1566 | if (IS_ERR(counters)) | ||
1567 | return PTR_ERR(counters); | ||
1568 | |||
1569 | /* choose the copy on our node/cpu */ | ||
1570 | loc_cpu_entry = private->entries[raw_smp_processor_id()]; | ||
1571 | pos = userptr; | ||
1572 | size = total_size; | ||
1573 | ret = ARPT_ENTRY_ITERATE(loc_cpu_entry, total_size, | ||
1574 | compat_copy_entry_to_user, | ||
1575 | &pos, &size, counters, &i); | ||
1576 | vfree(counters); | ||
1577 | return ret; | ||
1578 | } | ||
1579 | |||
1580 | struct compat_arpt_get_entries { | ||
1581 | char name[ARPT_TABLE_MAXNAMELEN]; | ||
1582 | compat_uint_t size; | ||
1583 | struct compat_arpt_entry entrytable[0]; | ||
1584 | }; | ||
1585 | |||
1586 | static int compat_get_entries(struct compat_arpt_get_entries __user *uptr, | ||
1587 | int *len) | ||
1588 | { | ||
1589 | int ret; | ||
1590 | struct compat_arpt_get_entries get; | ||
1591 | struct arpt_table *t; | ||
1592 | |||
1593 | if (*len < sizeof(get)) { | ||
1594 | duprintf("compat_get_entries: %u < %zu\n", *len, sizeof(get)); | ||
1595 | return -EINVAL; | ||
1596 | } | ||
1597 | if (copy_from_user(&get, uptr, sizeof(get)) != 0) | ||
1598 | return -EFAULT; | ||
1599 | if (*len != sizeof(struct compat_arpt_get_entries) + get.size) { | ||
1600 | duprintf("compat_get_entries: %u != %zu\n", | ||
1601 | *len, sizeof(get) + get.size); | ||
1602 | return -EINVAL; | ||
1603 | } | ||
1604 | |||
1605 | xt_compat_lock(NF_ARP); | ||
1606 | t = xt_find_table_lock(NF_ARP, get.name); | ||
1607 | if (t && !IS_ERR(t)) { | ||
1608 | struct xt_table_info *private = t->private; | ||
1609 | struct xt_table_info info; | ||
1610 | |||
1611 | duprintf("t->private->number = %u\n", private->number); | ||
1612 | ret = compat_table_info(private, &info); | ||
1613 | if (!ret && get.size == info.size) { | ||
1614 | ret = compat_copy_entries_to_user(private->size, | ||
1615 | t, uptr->entrytable); | ||
1616 | } else if (!ret) { | ||
1617 | duprintf("compat_get_entries: I've got %u not %u!\n", | ||
1618 | private->size, get.size); | ||
1619 | ret = -EINVAL; | ||
1620 | } | ||
1621 | xt_compat_flush_offsets(NF_ARP); | ||
1622 | module_put(t->me); | ||
1623 | xt_table_unlock(t); | ||
1624 | } else | ||
1625 | ret = t ? PTR_ERR(t) : -ENOENT; | ||
1626 | |||
1627 | xt_compat_unlock(NF_ARP); | ||
1628 | return ret; | ||
1629 | } | ||
1630 | |||
1631 | static int do_arpt_get_ctl(struct sock *, int, void __user *, int *); | ||
1632 | |||
1633 | static int compat_do_arpt_get_ctl(struct sock *sk, int cmd, void __user *user, | ||
1634 | int *len) | ||
1635 | { | ||
1636 | int ret; | ||
1637 | |||
1638 | if (!capable(CAP_NET_ADMIN)) | ||
1639 | return -EPERM; | ||
1640 | |||
1641 | switch (cmd) { | ||
1642 | case ARPT_SO_GET_INFO: | ||
1643 | ret = get_info(user, len, 1); | ||
1644 | break; | ||
1645 | case ARPT_SO_GET_ENTRIES: | ||
1646 | ret = compat_get_entries(user, len); | ||
1647 | break; | ||
1648 | default: | ||
1649 | ret = do_arpt_get_ctl(sk, cmd, user, len); | ||
1650 | } | ||
1651 | return ret; | ||
1652 | } | ||
1653 | #endif | ||
1654 | |||
1034 | static int do_arpt_set_ctl(struct sock *sk, int cmd, void __user *user, unsigned int len) | 1655 | static int do_arpt_set_ctl(struct sock *sk, int cmd, void __user *user, unsigned int len) |
1035 | { | 1656 | { |
1036 | int ret; | 1657 | int ret; |
@@ -1044,7 +1665,7 @@ static int do_arpt_set_ctl(struct sock *sk, int cmd, void __user *user, unsigned | |||
1044 | break; | 1665 | break; |
1045 | 1666 | ||
1046 | case ARPT_SO_SET_ADD_COUNTERS: | 1667 | case ARPT_SO_SET_ADD_COUNTERS: |
1047 | ret = do_add_counters(user, len); | 1668 | ret = do_add_counters(user, len, 0); |
1048 | break; | 1669 | break; |
1049 | 1670 | ||
1050 | default: | 1671 | default: |
@@ -1064,7 +1685,7 @@ static int do_arpt_get_ctl(struct sock *sk, int cmd, void __user *user, int *len | |||
1064 | 1685 | ||
1065 | switch (cmd) { | 1686 | switch (cmd) { |
1066 | case ARPT_SO_GET_INFO: | 1687 | case ARPT_SO_GET_INFO: |
1067 | ret = get_info(user, len); | 1688 | ret = get_info(user, len, 0); |
1068 | break; | 1689 | break; |
1069 | 1690 | ||
1070 | case ARPT_SO_GET_ENTRIES: | 1691 | case ARPT_SO_GET_ENTRIES: |
@@ -1156,6 +1777,11 @@ static struct arpt_target arpt_standard_target __read_mostly = { | |||
1156 | .name = ARPT_STANDARD_TARGET, | 1777 | .name = ARPT_STANDARD_TARGET, |
1157 | .targetsize = sizeof(int), | 1778 | .targetsize = sizeof(int), |
1158 | .family = NF_ARP, | 1779 | .family = NF_ARP, |
1780 | #ifdef CONFIG_COMPAT | ||
1781 | .compatsize = sizeof(compat_int_t), | ||
1782 | .compat_from_user = compat_standard_from_user, | ||
1783 | .compat_to_user = compat_standard_to_user, | ||
1784 | #endif | ||
1159 | }; | 1785 | }; |
1160 | 1786 | ||
1161 | static struct arpt_target arpt_error_target __read_mostly = { | 1787 | static struct arpt_target arpt_error_target __read_mostly = { |
@@ -1170,9 +1796,15 @@ static struct nf_sockopt_ops arpt_sockopts = { | |||
1170 | .set_optmin = ARPT_BASE_CTL, | 1796 | .set_optmin = ARPT_BASE_CTL, |
1171 | .set_optmax = ARPT_SO_SET_MAX+1, | 1797 | .set_optmax = ARPT_SO_SET_MAX+1, |
1172 | .set = do_arpt_set_ctl, | 1798 | .set = do_arpt_set_ctl, |
1799 | #ifdef CONFIG_COMPAT | ||
1800 | .compat_set = compat_do_arpt_set_ctl, | ||
1801 | #endif | ||
1173 | .get_optmin = ARPT_BASE_CTL, | 1802 | .get_optmin = ARPT_BASE_CTL, |
1174 | .get_optmax = ARPT_SO_GET_MAX+1, | 1803 | .get_optmax = ARPT_SO_GET_MAX+1, |
1175 | .get = do_arpt_get_ctl, | 1804 | .get = do_arpt_get_ctl, |
1805 | #ifdef CONFIG_COMPAT | ||
1806 | .compat_get = compat_do_arpt_get_ctl, | ||
1807 | #endif | ||
1176 | .owner = THIS_MODULE, | 1808 | .owner = THIS_MODULE, |
1177 | }; | 1809 | }; |
1178 | 1810 | ||