diff options
-rw-r--r-- | include/linux/netfilter_ipv6/ip6_tables.h | 35 | ||||
-rw-r--r-- | net/compat.c | 106 | ||||
-rw-r--r-- | net/ipv6/netfilter/ip6_tables.c | 823 |
3 files changed, 802 insertions, 162 deletions
diff --git a/include/linux/netfilter_ipv6/ip6_tables.h b/include/linux/netfilter_ipv6/ip6_tables.h index 8257b52015f3..c1124826bf29 100644 --- a/include/linux/netfilter_ipv6/ip6_tables.h +++ b/include/linux/netfilter_ipv6/ip6_tables.h | |||
@@ -326,5 +326,40 @@ extern int ip6_masked_addrcmp(const struct in6_addr *addr1, | |||
326 | 326 | ||
327 | #define IP6T_ALIGN(s) (((s) + (__alignof__(struct ip6t_entry)-1)) & ~(__alignof__(struct ip6t_entry)-1)) | 327 | #define IP6T_ALIGN(s) (((s) + (__alignof__(struct ip6t_entry)-1)) & ~(__alignof__(struct ip6t_entry)-1)) |
328 | 328 | ||
329 | #ifdef CONFIG_COMPAT | ||
330 | #include <net/compat.h> | ||
331 | |||
332 | struct compat_ip6t_entry | ||
333 | { | ||
334 | struct ip6t_ip6 ipv6; | ||
335 | compat_uint_t nfcache; | ||
336 | u_int16_t target_offset; | ||
337 | u_int16_t next_offset; | ||
338 | compat_uint_t comefrom; | ||
339 | struct compat_xt_counters counters; | ||
340 | unsigned char elems[0]; | ||
341 | }; | ||
342 | |||
343 | static inline struct ip6t_entry_target * | ||
344 | compat_ip6t_get_target(struct compat_ip6t_entry *e) | ||
345 | { | ||
346 | return (void *)e + e->target_offset; | ||
347 | } | ||
348 | |||
349 | #define COMPAT_IP6T_ALIGN(s) COMPAT_XT_ALIGN(s) | ||
350 | |||
351 | /* fn returns 0 to continue iteration */ | ||
352 | #define COMPAT_IP6T_MATCH_ITERATE(e, fn, args...) \ | ||
353 | XT_MATCH_ITERATE(struct compat_ip6t_entry, e, fn, ## args) | ||
354 | |||
355 | /* fn returns 0 to continue iteration */ | ||
356 | #define COMPAT_IP6T_ENTRY_ITERATE(entries, size, fn, args...) \ | ||
357 | XT_ENTRY_ITERATE(struct compat_ip6t_entry, entries, size, fn, ## args) | ||
358 | |||
359 | #define COMPAT_IP6T_ENTRY_ITERATE_CONTINUE(entries, size, n, fn, args...) \ | ||
360 | XT_ENTRY_ITERATE_CONTINUE(struct compat_ip6t_entry, entries, size, n, \ | ||
361 | fn, ## args) | ||
362 | |||
363 | #endif /* CONFIG_COMPAT */ | ||
329 | #endif /*__KERNEL__*/ | 364 | #endif /*__KERNEL__*/ |
330 | #endif /* _IP6_TABLES_H */ | 365 | #endif /* _IP6_TABLES_H */ |
diff --git a/net/compat.c b/net/compat.c index f4ef4c048652..80013fb69a61 100644 --- a/net/compat.c +++ b/net/compat.c | |||
@@ -20,7 +20,6 @@ | |||
20 | #include <linux/syscalls.h> | 20 | #include <linux/syscalls.h> |
21 | #include <linux/filter.h> | 21 | #include <linux/filter.h> |
22 | #include <linux/compat.h> | 22 | #include <linux/compat.h> |
23 | #include <linux/netfilter_ipv4/ip_tables.h> | ||
24 | #include <linux/security.h> | 23 | #include <linux/security.h> |
25 | 24 | ||
26 | #include <net/scm.h> | 25 | #include <net/scm.h> |
@@ -317,107 +316,6 @@ void scm_detach_fds_compat(struct msghdr *kmsg, struct scm_cookie *scm) | |||
317 | } | 316 | } |
318 | 317 | ||
319 | /* | 318 | /* |
320 | * For now, we assume that the compatibility and native version | ||
321 | * of struct ipt_entry are the same - sfr. FIXME | ||
322 | */ | ||
323 | struct compat_ipt_replace { | ||
324 | char name[IPT_TABLE_MAXNAMELEN]; | ||
325 | u32 valid_hooks; | ||
326 | u32 num_entries; | ||
327 | u32 size; | ||
328 | u32 hook_entry[NF_INET_NUMHOOKS]; | ||
329 | u32 underflow[NF_INET_NUMHOOKS]; | ||
330 | u32 num_counters; | ||
331 | compat_uptr_t counters; /* struct ipt_counters * */ | ||
332 | struct ipt_entry entries[0]; | ||
333 | }; | ||
334 | |||
335 | static int do_netfilter_replace(int fd, int level, int optname, | ||
336 | char __user *optval, int optlen) | ||
337 | { | ||
338 | struct compat_ipt_replace __user *urepl; | ||
339 | struct ipt_replace __user *repl_nat; | ||
340 | char name[IPT_TABLE_MAXNAMELEN]; | ||
341 | u32 origsize, tmp32, num_counters; | ||
342 | unsigned int repl_nat_size; | ||
343 | int ret; | ||
344 | int i; | ||
345 | compat_uptr_t ucntrs; | ||
346 | |||
347 | urepl = (struct compat_ipt_replace __user *)optval; | ||
348 | if (get_user(origsize, &urepl->size)) | ||
349 | return -EFAULT; | ||
350 | |||
351 | /* Hack: Causes ipchains to give correct error msg --RR */ | ||
352 | if (optlen != sizeof(*urepl) + origsize) | ||
353 | return -ENOPROTOOPT; | ||
354 | |||
355 | /* XXX Assumes that size of ipt_entry is the same both in | ||
356 | * native and compat environments. | ||
357 | */ | ||
358 | repl_nat_size = sizeof(*repl_nat) + origsize; | ||
359 | repl_nat = compat_alloc_user_space(repl_nat_size); | ||
360 | |||
361 | ret = -EFAULT; | ||
362 | if (put_user(origsize, &repl_nat->size)) | ||
363 | goto out; | ||
364 | |||
365 | if (!access_ok(VERIFY_READ, urepl, optlen) || | ||
366 | !access_ok(VERIFY_WRITE, repl_nat, optlen)) | ||
367 | goto out; | ||
368 | |||
369 | if (__copy_from_user(name, urepl->name, sizeof(urepl->name)) || | ||
370 | __copy_to_user(repl_nat->name, name, sizeof(repl_nat->name))) | ||
371 | goto out; | ||
372 | |||
373 | if (__get_user(tmp32, &urepl->valid_hooks) || | ||
374 | __put_user(tmp32, &repl_nat->valid_hooks)) | ||
375 | goto out; | ||
376 | |||
377 | if (__get_user(tmp32, &urepl->num_entries) || | ||
378 | __put_user(tmp32, &repl_nat->num_entries)) | ||
379 | goto out; | ||
380 | |||
381 | if (__get_user(num_counters, &urepl->num_counters) || | ||
382 | __put_user(num_counters, &repl_nat->num_counters)) | ||
383 | goto out; | ||
384 | |||
385 | if (__get_user(ucntrs, &urepl->counters) || | ||
386 | __put_user(compat_ptr(ucntrs), &repl_nat->counters)) | ||
387 | goto out; | ||
388 | |||
389 | if (__copy_in_user(&repl_nat->entries[0], | ||
390 | &urepl->entries[0], | ||
391 | origsize)) | ||
392 | goto out; | ||
393 | |||
394 | for (i = 0; i < NF_INET_NUMHOOKS; i++) { | ||
395 | if (__get_user(tmp32, &urepl->hook_entry[i]) || | ||
396 | __put_user(tmp32, &repl_nat->hook_entry[i]) || | ||
397 | __get_user(tmp32, &urepl->underflow[i]) || | ||
398 | __put_user(tmp32, &repl_nat->underflow[i])) | ||
399 | goto out; | ||
400 | } | ||
401 | |||
402 | /* | ||
403 | * Since struct ipt_counters just contains two u_int64_t members | ||
404 | * we can just do the access_ok check here and pass the (converted) | ||
405 | * pointer into the standard syscall. We hope that the pointer is | ||
406 | * not misaligned ... | ||
407 | */ | ||
408 | if (!access_ok(VERIFY_WRITE, compat_ptr(ucntrs), | ||
409 | num_counters * sizeof(struct ipt_counters))) | ||
410 | goto out; | ||
411 | |||
412 | |||
413 | ret = sys_setsockopt(fd, level, optname, | ||
414 | (char __user *)repl_nat, repl_nat_size); | ||
415 | |||
416 | out: | ||
417 | return ret; | ||
418 | } | ||
419 | |||
420 | /* | ||
421 | * A struct sock_filter is architecture independent. | 319 | * A struct sock_filter is architecture independent. |
422 | */ | 320 | */ |
423 | struct compat_sock_fprog { | 321 | struct compat_sock_fprog { |
@@ -485,10 +383,6 @@ asmlinkage long compat_sys_setsockopt(int fd, int level, int optname, | |||
485 | int err; | 383 | int err; |
486 | struct socket *sock; | 384 | struct socket *sock; |
487 | 385 | ||
488 | if (level == SOL_IPV6 && optname == IPT_SO_SET_REPLACE) | ||
489 | return do_netfilter_replace(fd, level, optname, | ||
490 | optval, optlen); | ||
491 | |||
492 | if (optlen < 0) | 386 | if (optlen < 0) |
493 | return -EINVAL; | 387 | return -EINVAL; |
494 | 388 | ||
diff --git a/net/ipv6/netfilter/ip6_tables.c b/net/ipv6/netfilter/ip6_tables.c index 6fcc0d5bc27f..db0dc96be55c 100644 --- a/net/ipv6/netfilter/ip6_tables.c +++ b/net/ipv6/netfilter/ip6_tables.c | |||
@@ -19,9 +19,11 @@ | |||
19 | #include <linux/poison.h> | 19 | #include <linux/poison.h> |
20 | #include <linux/icmpv6.h> | 20 | #include <linux/icmpv6.h> |
21 | #include <net/ipv6.h> | 21 | #include <net/ipv6.h> |
22 | #include <net/compat.h> | ||
22 | #include <asm/uaccess.h> | 23 | #include <asm/uaccess.h> |
23 | #include <linux/mutex.h> | 24 | #include <linux/mutex.h> |
24 | #include <linux/proc_fs.h> | 25 | #include <linux/proc_fs.h> |
26 | #include <linux/err.h> | ||
25 | #include <linux/cpumask.h> | 27 | #include <linux/cpumask.h> |
26 | 28 | ||
27 | #include <linux/netfilter_ipv6/ip6_tables.h> | 29 | #include <linux/netfilter_ipv6/ip6_tables.h> |
@@ -1037,7 +1039,80 @@ copy_entries_to_user(unsigned int total_size, | |||
1037 | return ret; | 1039 | return ret; |
1038 | } | 1040 | } |
1039 | 1041 | ||
1040 | static int get_info(void __user *user, int *len) | 1042 | #ifdef CONFIG_COMPAT |
1043 | static void compat_standard_from_user(void *dst, void *src) | ||
1044 | { | ||
1045 | int v = *(compat_int_t *)src; | ||
1046 | |||
1047 | if (v > 0) | ||
1048 | v += xt_compat_calc_jump(AF_INET6, v); | ||
1049 | memcpy(dst, &v, sizeof(v)); | ||
1050 | } | ||
1051 | |||
1052 | static int compat_standard_to_user(void __user *dst, void *src) | ||
1053 | { | ||
1054 | compat_int_t cv = *(int *)src; | ||
1055 | |||
1056 | if (cv > 0) | ||
1057 | cv -= xt_compat_calc_jump(AF_INET6, cv); | ||
1058 | return copy_to_user(dst, &cv, sizeof(cv)) ? -EFAULT : 0; | ||
1059 | } | ||
1060 | |||
1061 | static inline int | ||
1062 | compat_calc_match(struct ip6t_entry_match *m, int *size) | ||
1063 | { | ||
1064 | *size += xt_compat_match_offset(m->u.kernel.match); | ||
1065 | return 0; | ||
1066 | } | ||
1067 | |||
1068 | static int compat_calc_entry(struct ip6t_entry *e, | ||
1069 | const struct xt_table_info *info, | ||
1070 | void *base, struct xt_table_info *newinfo) | ||
1071 | { | ||
1072 | struct ip6t_entry_target *t; | ||
1073 | unsigned int entry_offset; | ||
1074 | int off, i, ret; | ||
1075 | |||
1076 | off = sizeof(struct ip6t_entry) - sizeof(struct compat_ip6t_entry); | ||
1077 | entry_offset = (void *)e - base; | ||
1078 | IP6T_MATCH_ITERATE(e, compat_calc_match, &off); | ||
1079 | t = ip6t_get_target(e); | ||
1080 | off += xt_compat_target_offset(t->u.kernel.target); | ||
1081 | newinfo->size -= off; | ||
1082 | ret = xt_compat_add_offset(AF_INET6, entry_offset, off); | ||
1083 | if (ret) | ||
1084 | return ret; | ||
1085 | |||
1086 | for (i = 0; i < NF_INET_NUMHOOKS; i++) { | ||
1087 | if (info->hook_entry[i] && | ||
1088 | (e < (struct ip6t_entry *)(base + info->hook_entry[i]))) | ||
1089 | newinfo->hook_entry[i] -= off; | ||
1090 | if (info->underflow[i] && | ||
1091 | (e < (struct ip6t_entry *)(base + info->underflow[i]))) | ||
1092 | newinfo->underflow[i] -= off; | ||
1093 | } | ||
1094 | return 0; | ||
1095 | } | ||
1096 | |||
1097 | static int compat_table_info(const struct xt_table_info *info, | ||
1098 | struct xt_table_info *newinfo) | ||
1099 | { | ||
1100 | void *loc_cpu_entry; | ||
1101 | |||
1102 | if (!newinfo || !info) | ||
1103 | return -EINVAL; | ||
1104 | |||
1105 | /* we dont care about newinfo->entries[] */ | ||
1106 | memcpy(newinfo, info, offsetof(struct xt_table_info, entries)); | ||
1107 | newinfo->initial_entries = 0; | ||
1108 | loc_cpu_entry = info->entries[raw_smp_processor_id()]; | ||
1109 | return IP6T_ENTRY_ITERATE(loc_cpu_entry, info->size, | ||
1110 | compat_calc_entry, info, loc_cpu_entry, | ||
1111 | newinfo); | ||
1112 | } | ||
1113 | #endif | ||
1114 | |||
1115 | static int get_info(void __user *user, int *len, int compat) | ||
1041 | { | 1116 | { |
1042 | char name[IP6T_TABLE_MAXNAMELEN]; | 1117 | char name[IP6T_TABLE_MAXNAMELEN]; |
1043 | struct xt_table *t; | 1118 | struct xt_table *t; |
@@ -1053,13 +1128,24 @@ static int get_info(void __user *user, int *len) | |||
1053 | return -EFAULT; | 1128 | return -EFAULT; |
1054 | 1129 | ||
1055 | name[IP6T_TABLE_MAXNAMELEN-1] = '\0'; | 1130 | name[IP6T_TABLE_MAXNAMELEN-1] = '\0'; |
1056 | 1131 | #ifdef CONFIG_COMPAT | |
1132 | if (compat) | ||
1133 | xt_compat_lock(AF_INET6); | ||
1134 | #endif | ||
1057 | t = try_then_request_module(xt_find_table_lock(AF_INET6, name), | 1135 | t = try_then_request_module(xt_find_table_lock(AF_INET6, name), |
1058 | "ip6table_%s", name); | 1136 | "ip6table_%s", name); |
1059 | if (t && !IS_ERR(t)) { | 1137 | if (t && !IS_ERR(t)) { |
1060 | struct ip6t_getinfo info; | 1138 | struct ip6t_getinfo info; |
1061 | struct xt_table_info *private = t->private; | 1139 | struct xt_table_info *private = t->private; |
1062 | 1140 | ||
1141 | #ifdef CONFIG_COMPAT | ||
1142 | if (compat) { | ||
1143 | struct xt_table_info tmp; | ||
1144 | ret = compat_table_info(private, &tmp); | ||
1145 | xt_compat_flush_offsets(AF_INET6); | ||
1146 | private = &tmp; | ||
1147 | } | ||
1148 | #endif | ||
1063 | info.valid_hooks = t->valid_hooks; | 1149 | info.valid_hooks = t->valid_hooks; |
1064 | memcpy(info.hook_entry, private->hook_entry, | 1150 | memcpy(info.hook_entry, private->hook_entry, |
1065 | sizeof(info.hook_entry)); | 1151 | sizeof(info.hook_entry)); |
@@ -1078,6 +1164,10 @@ static int get_info(void __user *user, int *len) | |||
1078 | module_put(t->me); | 1164 | module_put(t->me); |
1079 | } else | 1165 | } else |
1080 | ret = t ? PTR_ERR(t) : -ENOENT; | 1166 | ret = t ? PTR_ERR(t) : -ENOENT; |
1167 | #ifdef CONFIG_COMPAT | ||
1168 | if (compat) | ||
1169 | xt_compat_unlock(AF_INET6); | ||
1170 | #endif | ||
1081 | return ret; | 1171 | return ret; |
1082 | } | 1172 | } |
1083 | 1173 | ||
@@ -1121,65 +1211,40 @@ get_entries(struct ip6t_get_entries __user *uptr, int *len) | |||
1121 | } | 1211 | } |
1122 | 1212 | ||
1123 | static int | 1213 | static int |
1124 | do_replace(void __user *user, unsigned int len) | 1214 | __do_replace(const char *name, unsigned int valid_hooks, |
1215 | struct xt_table_info *newinfo, unsigned int num_counters, | ||
1216 | void __user *counters_ptr) | ||
1125 | { | 1217 | { |
1126 | int ret; | 1218 | int ret; |
1127 | struct ip6t_replace tmp; | ||
1128 | struct xt_table *t; | 1219 | struct xt_table *t; |
1129 | struct xt_table_info *newinfo, *oldinfo; | 1220 | struct xt_table_info *oldinfo; |
1130 | struct xt_counters *counters; | 1221 | struct xt_counters *counters; |
1131 | void *loc_cpu_entry, *loc_cpu_old_entry; | 1222 | void *loc_cpu_old_entry; |
1132 | |||
1133 | if (copy_from_user(&tmp, user, sizeof(tmp)) != 0) | ||
1134 | return -EFAULT; | ||
1135 | |||
1136 | /* overflow check */ | ||
1137 | if (tmp.num_counters >= INT_MAX / sizeof(struct xt_counters)) | ||
1138 | return -ENOMEM; | ||
1139 | |||
1140 | newinfo = xt_alloc_table_info(tmp.size); | ||
1141 | if (!newinfo) | ||
1142 | return -ENOMEM; | ||
1143 | |||
1144 | /* choose the copy that is on our node/cpu */ | ||
1145 | loc_cpu_entry = newinfo->entries[raw_smp_processor_id()]; | ||
1146 | if (copy_from_user(loc_cpu_entry, user + sizeof(tmp), | ||
1147 | tmp.size) != 0) { | ||
1148 | ret = -EFAULT; | ||
1149 | goto free_newinfo; | ||
1150 | } | ||
1151 | 1223 | ||
1152 | counters = vmalloc_node(tmp.num_counters * sizeof(struct xt_counters), | 1224 | ret = 0; |
1225 | counters = vmalloc_node(num_counters * sizeof(struct xt_counters), | ||
1153 | numa_node_id()); | 1226 | numa_node_id()); |
1154 | if (!counters) { | 1227 | if (!counters) { |
1155 | ret = -ENOMEM; | 1228 | ret = -ENOMEM; |
1156 | goto free_newinfo; | 1229 | goto out; |
1157 | } | 1230 | } |
1158 | 1231 | ||
1159 | ret = translate_table(tmp.name, tmp.valid_hooks, | 1232 | t = try_then_request_module(xt_find_table_lock(AF_INET6, name), |
1160 | newinfo, loc_cpu_entry, tmp.size, tmp.num_entries, | 1233 | "ip6table_%s", name); |
1161 | tmp.hook_entry, tmp.underflow); | ||
1162 | if (ret != 0) | ||
1163 | goto free_newinfo_counters; | ||
1164 | |||
1165 | duprintf("ip_tables: Translated table\n"); | ||
1166 | |||
1167 | t = try_then_request_module(xt_find_table_lock(AF_INET6, tmp.name), | ||
1168 | "ip6table_%s", tmp.name); | ||
1169 | if (!t || IS_ERR(t)) { | 1234 | if (!t || IS_ERR(t)) { |
1170 | ret = t ? PTR_ERR(t) : -ENOENT; | 1235 | ret = t ? PTR_ERR(t) : -ENOENT; |
1171 | goto free_newinfo_counters_untrans; | 1236 | goto free_newinfo_counters_untrans; |
1172 | } | 1237 | } |
1173 | 1238 | ||
1174 | /* You lied! */ | 1239 | /* You lied! */ |
1175 | if (tmp.valid_hooks != t->valid_hooks) { | 1240 | if (valid_hooks != t->valid_hooks) { |
1176 | duprintf("Valid hook crap: %08X vs %08X\n", | 1241 | duprintf("Valid hook crap: %08X vs %08X\n", |
1177 | tmp.valid_hooks, t->valid_hooks); | 1242 | valid_hooks, t->valid_hooks); |
1178 | ret = -EINVAL; | 1243 | ret = -EINVAL; |
1179 | goto put_module; | 1244 | goto put_module; |
1180 | } | 1245 | } |
1181 | 1246 | ||
1182 | oldinfo = xt_replace_table(t, tmp.num_counters, newinfo, &ret); | 1247 | oldinfo = xt_replace_table(t, num_counters, newinfo, &ret); |
1183 | if (!oldinfo) | 1248 | if (!oldinfo) |
1184 | goto put_module; | 1249 | goto put_module; |
1185 | 1250 | ||
@@ -1197,10 +1262,11 @@ do_replace(void __user *user, unsigned int len) | |||
1197 | get_counters(oldinfo, counters); | 1262 | get_counters(oldinfo, counters); |
1198 | /* Decrease module usage counts and free resource */ | 1263 | /* Decrease module usage counts and free resource */ |
1199 | loc_cpu_old_entry = oldinfo->entries[raw_smp_processor_id()]; | 1264 | loc_cpu_old_entry = oldinfo->entries[raw_smp_processor_id()]; |
1200 | IP6T_ENTRY_ITERATE(loc_cpu_old_entry, oldinfo->size, cleanup_entry,NULL); | 1265 | IP6T_ENTRY_ITERATE(loc_cpu_old_entry, oldinfo->size, cleanup_entry, |
1266 | NULL); | ||
1201 | xt_free_table_info(oldinfo); | 1267 | xt_free_table_info(oldinfo); |
1202 | if (copy_to_user(tmp.counters, counters, | 1268 | if (copy_to_user(counters_ptr, counters, |
1203 | sizeof(struct xt_counters) * tmp.num_counters) != 0) | 1269 | sizeof(struct xt_counters) * num_counters) != 0) |
1204 | ret = -EFAULT; | 1270 | ret = -EFAULT; |
1205 | vfree(counters); | 1271 | vfree(counters); |
1206 | xt_table_unlock(t); | 1272 | xt_table_unlock(t); |
@@ -1210,9 +1276,54 @@ do_replace(void __user *user, unsigned int len) | |||
1210 | module_put(t->me); | 1276 | module_put(t->me); |
1211 | xt_table_unlock(t); | 1277 | xt_table_unlock(t); |
1212 | free_newinfo_counters_untrans: | 1278 | free_newinfo_counters_untrans: |
1213 | IP6T_ENTRY_ITERATE(loc_cpu_entry, newinfo->size, cleanup_entry,NULL); | ||
1214 | free_newinfo_counters: | ||
1215 | vfree(counters); | 1279 | vfree(counters); |
1280 | out: | ||
1281 | return ret; | ||
1282 | } | ||
1283 | |||
1284 | static int | ||
1285 | do_replace(void __user *user, unsigned int len) | ||
1286 | { | ||
1287 | int ret; | ||
1288 | struct ip6t_replace tmp; | ||
1289 | struct xt_table_info *newinfo; | ||
1290 | void *loc_cpu_entry; | ||
1291 | |||
1292 | if (copy_from_user(&tmp, user, sizeof(tmp)) != 0) | ||
1293 | return -EFAULT; | ||
1294 | |||
1295 | /* overflow check */ | ||
1296 | if (tmp.num_counters >= INT_MAX / sizeof(struct xt_counters)) | ||
1297 | return -ENOMEM; | ||
1298 | |||
1299 | newinfo = xt_alloc_table_info(tmp.size); | ||
1300 | if (!newinfo) | ||
1301 | return -ENOMEM; | ||
1302 | |||
1303 | /* choose the copy that is on our node/cpu */ | ||
1304 | loc_cpu_entry = newinfo->entries[raw_smp_processor_id()]; | ||
1305 | if (copy_from_user(loc_cpu_entry, user + sizeof(tmp), | ||
1306 | tmp.size) != 0) { | ||
1307 | ret = -EFAULT; | ||
1308 | goto free_newinfo; | ||
1309 | } | ||
1310 | |||
1311 | ret = translate_table(tmp.name, tmp.valid_hooks, | ||
1312 | newinfo, loc_cpu_entry, tmp.size, tmp.num_entries, | ||
1313 | tmp.hook_entry, tmp.underflow); | ||
1314 | if (ret != 0) | ||
1315 | goto free_newinfo; | ||
1316 | |||
1317 | duprintf("ip_tables: Translated table\n"); | ||
1318 | |||
1319 | ret = __do_replace(tmp.name, tmp.valid_hooks, newinfo, | ||
1320 | tmp.num_counters, tmp.counters); | ||
1321 | if (ret) | ||
1322 | goto free_newinfo_untrans; | ||
1323 | return 0; | ||
1324 | |||
1325 | free_newinfo_untrans: | ||
1326 | IP6T_ENTRY_ITERATE(loc_cpu_entry, newinfo->size, cleanup_entry, NULL); | ||
1216 | free_newinfo: | 1327 | free_newinfo: |
1217 | xt_free_table_info(newinfo); | 1328 | xt_free_table_info(newinfo); |
1218 | return ret; | 1329 | return ret; |
@@ -1241,31 +1352,59 @@ add_counter_to_entry(struct ip6t_entry *e, | |||
1241 | } | 1352 | } |
1242 | 1353 | ||
1243 | static int | 1354 | static int |
1244 | do_add_counters(void __user *user, unsigned int len) | 1355 | do_add_counters(void __user *user, unsigned int len, int compat) |
1245 | { | 1356 | { |
1246 | unsigned int i; | 1357 | unsigned int i; |
1247 | struct xt_counters_info tmp, *paddc; | 1358 | struct xt_counters_info tmp; |
1359 | struct xt_counters *paddc; | ||
1360 | unsigned int num_counters; | ||
1361 | char *name; | ||
1362 | int size; | ||
1363 | void *ptmp; | ||
1248 | struct xt_table_info *private; | 1364 | struct xt_table_info *private; |
1249 | struct xt_table *t; | 1365 | struct xt_table *t; |
1250 | int ret = 0; | 1366 | int ret = 0; |
1251 | void *loc_cpu_entry; | 1367 | void *loc_cpu_entry; |
1368 | #ifdef CONFIG_COMPAT | ||
1369 | struct compat_xt_counters_info compat_tmp; | ||
1252 | 1370 | ||
1253 | if (copy_from_user(&tmp, user, sizeof(tmp)) != 0) | 1371 | if (compat) { |
1372 | ptmp = &compat_tmp; | ||
1373 | size = sizeof(struct compat_xt_counters_info); | ||
1374 | } else | ||
1375 | #endif | ||
1376 | { | ||
1377 | ptmp = &tmp; | ||
1378 | size = sizeof(struct xt_counters_info); | ||
1379 | } | ||
1380 | |||
1381 | if (copy_from_user(ptmp, user, size) != 0) | ||
1254 | return -EFAULT; | 1382 | return -EFAULT; |
1255 | 1383 | ||
1256 | if (len != sizeof(tmp) + tmp.num_counters*sizeof(struct xt_counters)) | 1384 | #ifdef CONFIG_COMPAT |
1385 | if (compat) { | ||
1386 | num_counters = compat_tmp.num_counters; | ||
1387 | name = compat_tmp.name; | ||
1388 | } else | ||
1389 | #endif | ||
1390 | { | ||
1391 | num_counters = tmp.num_counters; | ||
1392 | name = tmp.name; | ||
1393 | } | ||
1394 | |||
1395 | if (len != size + num_counters * sizeof(struct xt_counters)) | ||
1257 | return -EINVAL; | 1396 | return -EINVAL; |
1258 | 1397 | ||
1259 | paddc = vmalloc_node(len, numa_node_id()); | 1398 | paddc = vmalloc_node(len - size, numa_node_id()); |
1260 | if (!paddc) | 1399 | if (!paddc) |
1261 | return -ENOMEM; | 1400 | return -ENOMEM; |
1262 | 1401 | ||
1263 | if (copy_from_user(paddc, user, len) != 0) { | 1402 | if (copy_from_user(paddc, user + size, len - size) != 0) { |
1264 | ret = -EFAULT; | 1403 | ret = -EFAULT; |
1265 | goto free; | 1404 | goto free; |
1266 | } | 1405 | } |
1267 | 1406 | ||
1268 | t = xt_find_table_lock(AF_INET6, tmp.name); | 1407 | t = xt_find_table_lock(AF_INET6, name); |
1269 | if (!t || IS_ERR(t)) { | 1408 | if (!t || IS_ERR(t)) { |
1270 | ret = t ? PTR_ERR(t) : -ENOENT; | 1409 | ret = t ? PTR_ERR(t) : -ENOENT; |
1271 | goto free; | 1410 | goto free; |
@@ -1273,7 +1412,7 @@ do_add_counters(void __user *user, unsigned int len) | |||
1273 | 1412 | ||
1274 | write_lock_bh(&t->lock); | 1413 | write_lock_bh(&t->lock); |
1275 | private = t->private; | 1414 | private = t->private; |
1276 | if (private->number != tmp.num_counters) { | 1415 | if (private->number != num_counters) { |
1277 | ret = -EINVAL; | 1416 | ret = -EINVAL; |
1278 | goto unlock_up_free; | 1417 | goto unlock_up_free; |
1279 | } | 1418 | } |
@@ -1284,7 +1423,7 @@ do_add_counters(void __user *user, unsigned int len) | |||
1284 | IP6T_ENTRY_ITERATE(loc_cpu_entry, | 1423 | IP6T_ENTRY_ITERATE(loc_cpu_entry, |
1285 | private->size, | 1424 | private->size, |
1286 | add_counter_to_entry, | 1425 | add_counter_to_entry, |
1287 | paddc->counters, | 1426 | paddc, |
1288 | &i); | 1427 | &i); |
1289 | unlock_up_free: | 1428 | unlock_up_free: |
1290 | write_unlock_bh(&t->lock); | 1429 | write_unlock_bh(&t->lock); |
@@ -1296,6 +1435,567 @@ do_add_counters(void __user *user, unsigned int len) | |||
1296 | return ret; | 1435 | return ret; |
1297 | } | 1436 | } |
1298 | 1437 | ||
1438 | #ifdef CONFIG_COMPAT | ||
1439 | struct compat_ip6t_replace { | ||
1440 | char name[IP6T_TABLE_MAXNAMELEN]; | ||
1441 | u32 valid_hooks; | ||
1442 | u32 num_entries; | ||
1443 | u32 size; | ||
1444 | u32 hook_entry[NF_INET_NUMHOOKS]; | ||
1445 | u32 underflow[NF_INET_NUMHOOKS]; | ||
1446 | u32 num_counters; | ||
1447 | compat_uptr_t counters; /* struct ip6t_counters * */ | ||
1448 | struct compat_ip6t_entry entries[0]; | ||
1449 | }; | ||
1450 | |||
1451 | static int | ||
1452 | compat_copy_entry_to_user(struct ip6t_entry *e, void __user **dstptr, | ||
1453 | compat_uint_t *size, struct xt_counters *counters, | ||
1454 | unsigned int *i) | ||
1455 | { | ||
1456 | struct ip6t_entry_target *t; | ||
1457 | struct compat_ip6t_entry __user *ce; | ||
1458 | u_int16_t target_offset, next_offset; | ||
1459 | compat_uint_t origsize; | ||
1460 | int ret; | ||
1461 | |||
1462 | ret = -EFAULT; | ||
1463 | origsize = *size; | ||
1464 | ce = (struct compat_ip6t_entry __user *)*dstptr; | ||
1465 | if (copy_to_user(ce, e, sizeof(struct ip6t_entry))) | ||
1466 | goto out; | ||
1467 | |||
1468 | if (copy_to_user(&ce->counters, &counters[*i], sizeof(counters[*i]))) | ||
1469 | goto out; | ||
1470 | |||
1471 | *dstptr += sizeof(struct compat_ip6t_entry); | ||
1472 | *size -= sizeof(struct ip6t_entry) - sizeof(struct compat_ip6t_entry); | ||
1473 | |||
1474 | ret = IP6T_MATCH_ITERATE(e, xt_compat_match_to_user, dstptr, size); | ||
1475 | target_offset = e->target_offset - (origsize - *size); | ||
1476 | if (ret) | ||
1477 | goto out; | ||
1478 | t = ip6t_get_target(e); | ||
1479 | ret = xt_compat_target_to_user(t, dstptr, size); | ||
1480 | if (ret) | ||
1481 | goto out; | ||
1482 | ret = -EFAULT; | ||
1483 | next_offset = e->next_offset - (origsize - *size); | ||
1484 | if (put_user(target_offset, &ce->target_offset)) | ||
1485 | goto out; | ||
1486 | if (put_user(next_offset, &ce->next_offset)) | ||
1487 | goto out; | ||
1488 | |||
1489 | (*i)++; | ||
1490 | return 0; | ||
1491 | out: | ||
1492 | return ret; | ||
1493 | } | ||
1494 | |||
1495 | static inline int | ||
1496 | compat_find_calc_match(struct ip6t_entry_match *m, | ||
1497 | const char *name, | ||
1498 | const struct ip6t_ip6 *ipv6, | ||
1499 | unsigned int hookmask, | ||
1500 | int *size, int *i) | ||
1501 | { | ||
1502 | struct xt_match *match; | ||
1503 | |||
1504 | match = try_then_request_module(xt_find_match(AF_INET6, m->u.user.name, | ||
1505 | m->u.user.revision), | ||
1506 | "ip6t_%s", m->u.user.name); | ||
1507 | if (IS_ERR(match) || !match) { | ||
1508 | duprintf("compat_check_calc_match: `%s' not found\n", | ||
1509 | m->u.user.name); | ||
1510 | return match ? PTR_ERR(match) : -ENOENT; | ||
1511 | } | ||
1512 | m->u.kernel.match = match; | ||
1513 | *size += xt_compat_match_offset(match); | ||
1514 | |||
1515 | (*i)++; | ||
1516 | return 0; | ||
1517 | } | ||
1518 | |||
1519 | static inline int | ||
1520 | compat_release_match(struct ip6t_entry_match *m, unsigned int *i) | ||
1521 | { | ||
1522 | if (i && (*i)-- == 0) | ||
1523 | return 1; | ||
1524 | |||
1525 | module_put(m->u.kernel.match->me); | ||
1526 | return 0; | ||
1527 | } | ||
1528 | |||
1529 | static inline int | ||
1530 | compat_release_entry(struct compat_ip6t_entry *e, unsigned int *i) | ||
1531 | { | ||
1532 | struct ip6t_entry_target *t; | ||
1533 | |||
1534 | if (i && (*i)-- == 0) | ||
1535 | return 1; | ||
1536 | |||
1537 | /* Cleanup all matches */ | ||
1538 | COMPAT_IP6T_MATCH_ITERATE(e, compat_release_match, NULL); | ||
1539 | t = compat_ip6t_get_target(e); | ||
1540 | module_put(t->u.kernel.target->me); | ||
1541 | return 0; | ||
1542 | } | ||
1543 | |||
1544 | static inline int | ||
1545 | check_compat_entry_size_and_hooks(struct compat_ip6t_entry *e, | ||
1546 | struct xt_table_info *newinfo, | ||
1547 | unsigned int *size, | ||
1548 | unsigned char *base, | ||
1549 | unsigned char *limit, | ||
1550 | unsigned int *hook_entries, | ||
1551 | unsigned int *underflows, | ||
1552 | unsigned int *i, | ||
1553 | const char *name) | ||
1554 | { | ||
1555 | struct ip6t_entry_target *t; | ||
1556 | struct xt_target *target; | ||
1557 | unsigned int entry_offset; | ||
1558 | int ret, off, h, j; | ||
1559 | |||
1560 | duprintf("check_compat_entry_size_and_hooks %p\n", e); | ||
1561 | if ((unsigned long)e % __alignof__(struct compat_ip6t_entry) != 0 | ||
1562 | || (unsigned char *)e + sizeof(struct compat_ip6t_entry) >= limit) { | ||
1563 | duprintf("Bad offset %p, limit = %p\n", e, limit); | ||
1564 | return -EINVAL; | ||
1565 | } | ||
1566 | |||
1567 | if (e->next_offset < sizeof(struct compat_ip6t_entry) + | ||
1568 | sizeof(struct compat_xt_entry_target)) { | ||
1569 | duprintf("checking: element %p size %u\n", | ||
1570 | e, e->next_offset); | ||
1571 | return -EINVAL; | ||
1572 | } | ||
1573 | |||
1574 | /* For purposes of check_entry casting the compat entry is fine */ | ||
1575 | ret = check_entry((struct ip6t_entry *)e, name); | ||
1576 | if (ret) | ||
1577 | return ret; | ||
1578 | |||
1579 | off = sizeof(struct ip6t_entry) - sizeof(struct compat_ip6t_entry); | ||
1580 | entry_offset = (void *)e - (void *)base; | ||
1581 | j = 0; | ||
1582 | ret = COMPAT_IP6T_MATCH_ITERATE(e, compat_find_calc_match, name, | ||
1583 | &e->ipv6, e->comefrom, &off, &j); | ||
1584 | if (ret != 0) | ||
1585 | goto release_matches; | ||
1586 | |||
1587 | t = compat_ip6t_get_target(e); | ||
1588 | target = try_then_request_module(xt_find_target(AF_INET6, | ||
1589 | t->u.user.name, | ||
1590 | t->u.user.revision), | ||
1591 | "ip6t_%s", t->u.user.name); | ||
1592 | if (IS_ERR(target) || !target) { | ||
1593 | duprintf("check_compat_entry_size_and_hooks: `%s' not found\n", | ||
1594 | t->u.user.name); | ||
1595 | ret = target ? PTR_ERR(target) : -ENOENT; | ||
1596 | goto release_matches; | ||
1597 | } | ||
1598 | t->u.kernel.target = target; | ||
1599 | |||
1600 | off += xt_compat_target_offset(target); | ||
1601 | *size += off; | ||
1602 | ret = xt_compat_add_offset(AF_INET6, entry_offset, off); | ||
1603 | if (ret) | ||
1604 | goto out; | ||
1605 | |||
1606 | /* Check hooks & underflows */ | ||
1607 | for (h = 0; h < NF_INET_NUMHOOKS; h++) { | ||
1608 | if ((unsigned char *)e - base == hook_entries[h]) | ||
1609 | newinfo->hook_entry[h] = hook_entries[h]; | ||
1610 | if ((unsigned char *)e - base == underflows[h]) | ||
1611 | newinfo->underflow[h] = underflows[h]; | ||
1612 | } | ||
1613 | |||
1614 | /* Clear counters and comefrom */ | ||
1615 | memset(&e->counters, 0, sizeof(e->counters)); | ||
1616 | e->comefrom = 0; | ||
1617 | |||
1618 | (*i)++; | ||
1619 | return 0; | ||
1620 | |||
1621 | out: | ||
1622 | module_put(t->u.kernel.target->me); | ||
1623 | release_matches: | ||
1624 | IP6T_MATCH_ITERATE(e, compat_release_match, &j); | ||
1625 | return ret; | ||
1626 | } | ||
1627 | |||
1628 | static int | ||
1629 | compat_copy_entry_from_user(struct compat_ip6t_entry *e, void **dstptr, | ||
1630 | unsigned int *size, const char *name, | ||
1631 | struct xt_table_info *newinfo, unsigned char *base) | ||
1632 | { | ||
1633 | struct ip6t_entry_target *t; | ||
1634 | struct xt_target *target; | ||
1635 | struct ip6t_entry *de; | ||
1636 | unsigned int origsize; | ||
1637 | int ret, h; | ||
1638 | |||
1639 | ret = 0; | ||
1640 | origsize = *size; | ||
1641 | de = (struct ip6t_entry *)*dstptr; | ||
1642 | memcpy(de, e, sizeof(struct ip6t_entry)); | ||
1643 | memcpy(&de->counters, &e->counters, sizeof(e->counters)); | ||
1644 | |||
1645 | *dstptr += sizeof(struct ip6t_entry); | ||
1646 | *size += sizeof(struct ip6t_entry) - sizeof(struct compat_ip6t_entry); | ||
1647 | |||
1648 | ret = COMPAT_IP6T_MATCH_ITERATE(e, xt_compat_match_from_user, | ||
1649 | dstptr, size); | ||
1650 | if (ret) | ||
1651 | return ret; | ||
1652 | de->target_offset = e->target_offset - (origsize - *size); | ||
1653 | t = compat_ip6t_get_target(e); | ||
1654 | target = t->u.kernel.target; | ||
1655 | xt_compat_target_from_user(t, dstptr, size); | ||
1656 | |||
1657 | de->next_offset = e->next_offset - (origsize - *size); | ||
1658 | for (h = 0; h < NF_INET_NUMHOOKS; h++) { | ||
1659 | if ((unsigned char *)de - base < newinfo->hook_entry[h]) | ||
1660 | newinfo->hook_entry[h] -= origsize - *size; | ||
1661 | if ((unsigned char *)de - base < newinfo->underflow[h]) | ||
1662 | newinfo->underflow[h] -= origsize - *size; | ||
1663 | } | ||
1664 | return ret; | ||
1665 | } | ||
1666 | |||
1667 | static inline int compat_check_entry(struct ip6t_entry *e, const char *name, | ||
1668 | unsigned int *i) | ||
1669 | { | ||
1670 | int j, ret; | ||
1671 | |||
1672 | j = 0; | ||
1673 | ret = IP6T_MATCH_ITERATE(e, check_match, name, &e->ipv6, | ||
1674 | e->comefrom, &j); | ||
1675 | if (ret) | ||
1676 | goto cleanup_matches; | ||
1677 | |||
1678 | ret = check_target(e, name); | ||
1679 | if (ret) | ||
1680 | goto cleanup_matches; | ||
1681 | |||
1682 | (*i)++; | ||
1683 | return 0; | ||
1684 | |||
1685 | cleanup_matches: | ||
1686 | IP6T_MATCH_ITERATE(e, cleanup_match, &j); | ||
1687 | return ret; | ||
1688 | } | ||
1689 | |||
1690 | static int | ||
1691 | translate_compat_table(const char *name, | ||
1692 | unsigned int valid_hooks, | ||
1693 | struct xt_table_info **pinfo, | ||
1694 | void **pentry0, | ||
1695 | unsigned int total_size, | ||
1696 | unsigned int number, | ||
1697 | unsigned int *hook_entries, | ||
1698 | unsigned int *underflows) | ||
1699 | { | ||
1700 | unsigned int i, j; | ||
1701 | struct xt_table_info *newinfo, *info; | ||
1702 | void *pos, *entry0, *entry1; | ||
1703 | unsigned int size; | ||
1704 | int ret; | ||
1705 | |||
1706 | info = *pinfo; | ||
1707 | entry0 = *pentry0; | ||
1708 | size = total_size; | ||
1709 | info->number = number; | ||
1710 | |||
1711 | /* Init all hooks to impossible value. */ | ||
1712 | for (i = 0; i < NF_INET_NUMHOOKS; i++) { | ||
1713 | info->hook_entry[i] = 0xFFFFFFFF; | ||
1714 | info->underflow[i] = 0xFFFFFFFF; | ||
1715 | } | ||
1716 | |||
1717 | duprintf("translate_compat_table: size %u\n", info->size); | ||
1718 | j = 0; | ||
1719 | xt_compat_lock(AF_INET6); | ||
1720 | /* Walk through entries, checking offsets. */ | ||
1721 | ret = COMPAT_IP6T_ENTRY_ITERATE(entry0, total_size, | ||
1722 | check_compat_entry_size_and_hooks, | ||
1723 | info, &size, entry0, | ||
1724 | entry0 + total_size, | ||
1725 | hook_entries, underflows, &j, name); | ||
1726 | if (ret != 0) | ||
1727 | goto out_unlock; | ||
1728 | |||
1729 | ret = -EINVAL; | ||
1730 | if (j != number) { | ||
1731 | duprintf("translate_compat_table: %u not %u entries\n", | ||
1732 | j, number); | ||
1733 | goto out_unlock; | ||
1734 | } | ||
1735 | |||
1736 | /* Check hooks all assigned */ | ||
1737 | for (i = 0; i < NF_INET_NUMHOOKS; i++) { | ||
1738 | /* Only hooks which are valid */ | ||
1739 | if (!(valid_hooks & (1 << i))) | ||
1740 | continue; | ||
1741 | if (info->hook_entry[i] == 0xFFFFFFFF) { | ||
1742 | duprintf("Invalid hook entry %u %u\n", | ||
1743 | i, hook_entries[i]); | ||
1744 | goto out_unlock; | ||
1745 | } | ||
1746 | if (info->underflow[i] == 0xFFFFFFFF) { | ||
1747 | duprintf("Invalid underflow %u %u\n", | ||
1748 | i, underflows[i]); | ||
1749 | goto out_unlock; | ||
1750 | } | ||
1751 | } | ||
1752 | |||
1753 | ret = -ENOMEM; | ||
1754 | newinfo = xt_alloc_table_info(size); | ||
1755 | if (!newinfo) | ||
1756 | goto out_unlock; | ||
1757 | |||
1758 | newinfo->number = number; | ||
1759 | for (i = 0; i < NF_INET_NUMHOOKS; i++) { | ||
1760 | newinfo->hook_entry[i] = info->hook_entry[i]; | ||
1761 | newinfo->underflow[i] = info->underflow[i]; | ||
1762 | } | ||
1763 | entry1 = newinfo->entries[raw_smp_processor_id()]; | ||
1764 | pos = entry1; | ||
1765 | size = total_size; | ||
1766 | ret = COMPAT_IP6T_ENTRY_ITERATE(entry0, total_size, | ||
1767 | compat_copy_entry_from_user, | ||
1768 | &pos, &size, name, newinfo, entry1); | ||
1769 | xt_compat_flush_offsets(AF_INET6); | ||
1770 | xt_compat_unlock(AF_INET6); | ||
1771 | if (ret) | ||
1772 | goto free_newinfo; | ||
1773 | |||
1774 | ret = -ELOOP; | ||
1775 | if (!mark_source_chains(newinfo, valid_hooks, entry1)) | ||
1776 | goto free_newinfo; | ||
1777 | |||
1778 | i = 0; | ||
1779 | ret = IP6T_ENTRY_ITERATE(entry1, newinfo->size, compat_check_entry, | ||
1780 | name, &i); | ||
1781 | if (ret) { | ||
1782 | j -= i; | ||
1783 | COMPAT_IP6T_ENTRY_ITERATE_CONTINUE(entry0, newinfo->size, i, | ||
1784 | compat_release_entry, &j); | ||
1785 | IP6T_ENTRY_ITERATE(entry1, newinfo->size, cleanup_entry, &i); | ||
1786 | xt_free_table_info(newinfo); | ||
1787 | return ret; | ||
1788 | } | ||
1789 | |||
1790 | /* And one copy for every other CPU */ | ||
1791 | for_each_possible_cpu(i) | ||
1792 | if (newinfo->entries[i] && newinfo->entries[i] != entry1) | ||
1793 | memcpy(newinfo->entries[i], entry1, newinfo->size); | ||
1794 | |||
1795 | *pinfo = newinfo; | ||
1796 | *pentry0 = entry1; | ||
1797 | xt_free_table_info(info); | ||
1798 | return 0; | ||
1799 | |||
1800 | free_newinfo: | ||
1801 | xt_free_table_info(newinfo); | ||
1802 | out: | ||
1803 | COMPAT_IP6T_ENTRY_ITERATE(entry0, total_size, compat_release_entry, &j); | ||
1804 | return ret; | ||
1805 | out_unlock: | ||
1806 | xt_compat_flush_offsets(AF_INET6); | ||
1807 | xt_compat_unlock(AF_INET6); | ||
1808 | goto out; | ||
1809 | } | ||
1810 | |||
1811 | static int | ||
1812 | compat_do_replace(void __user *user, unsigned int len) | ||
1813 | { | ||
1814 | int ret; | ||
1815 | struct compat_ip6t_replace tmp; | ||
1816 | struct xt_table_info *newinfo; | ||
1817 | void *loc_cpu_entry; | ||
1818 | |||
1819 | if (copy_from_user(&tmp, user, sizeof(tmp)) != 0) | ||
1820 | return -EFAULT; | ||
1821 | |||
1822 | /* overflow check */ | ||
1823 | if (tmp.size >= INT_MAX / num_possible_cpus()) | ||
1824 | return -ENOMEM; | ||
1825 | if (tmp.num_counters >= INT_MAX / sizeof(struct xt_counters)) | ||
1826 | return -ENOMEM; | ||
1827 | |||
1828 | newinfo = xt_alloc_table_info(tmp.size); | ||
1829 | if (!newinfo) | ||
1830 | return -ENOMEM; | ||
1831 | |||
1832 | /* choose the copy that is our node/cpu */ | ||
1833 | loc_cpu_entry = newinfo->entries[raw_smp_processor_id()]; | ||
1834 | if (copy_from_user(loc_cpu_entry, user + sizeof(tmp), | ||
1835 | tmp.size) != 0) { | ||
1836 | ret = -EFAULT; | ||
1837 | goto free_newinfo; | ||
1838 | } | ||
1839 | |||
1840 | ret = translate_compat_table(tmp.name, tmp.valid_hooks, | ||
1841 | &newinfo, &loc_cpu_entry, tmp.size, | ||
1842 | tmp.num_entries, tmp.hook_entry, | ||
1843 | tmp.underflow); | ||
1844 | if (ret != 0) | ||
1845 | goto free_newinfo; | ||
1846 | |||
1847 | duprintf("compat_do_replace: Translated table\n"); | ||
1848 | |||
1849 | ret = __do_replace(tmp.name, tmp.valid_hooks, newinfo, | ||
1850 | tmp.num_counters, compat_ptr(tmp.counters)); | ||
1851 | if (ret) | ||
1852 | goto free_newinfo_untrans; | ||
1853 | return 0; | ||
1854 | |||
1855 | free_newinfo_untrans: | ||
1856 | IP6T_ENTRY_ITERATE(loc_cpu_entry, newinfo->size, cleanup_entry, NULL); | ||
1857 | free_newinfo: | ||
1858 | xt_free_table_info(newinfo); | ||
1859 | return ret; | ||
1860 | } | ||
1861 | |||
1862 | static int | ||
1863 | compat_do_ip6t_set_ctl(struct sock *sk, int cmd, void __user *user, | ||
1864 | unsigned int len) | ||
1865 | { | ||
1866 | int ret; | ||
1867 | |||
1868 | if (!capable(CAP_NET_ADMIN)) | ||
1869 | return -EPERM; | ||
1870 | |||
1871 | switch (cmd) { | ||
1872 | case IP6T_SO_SET_REPLACE: | ||
1873 | ret = compat_do_replace(user, len); | ||
1874 | break; | ||
1875 | |||
1876 | case IP6T_SO_SET_ADD_COUNTERS: | ||
1877 | ret = do_add_counters(user, len, 1); | ||
1878 | break; | ||
1879 | |||
1880 | default: | ||
1881 | duprintf("do_ip6t_set_ctl: unknown request %i\n", cmd); | ||
1882 | ret = -EINVAL; | ||
1883 | } | ||
1884 | |||
1885 | return ret; | ||
1886 | } | ||
1887 | |||
1888 | struct compat_ip6t_get_entries { | ||
1889 | char name[IP6T_TABLE_MAXNAMELEN]; | ||
1890 | compat_uint_t size; | ||
1891 | struct compat_ip6t_entry entrytable[0]; | ||
1892 | }; | ||
1893 | |||
1894 | static int | ||
1895 | compat_copy_entries_to_user(unsigned int total_size, struct xt_table *table, | ||
1896 | void __user *userptr) | ||
1897 | { | ||
1898 | struct xt_counters *counters; | ||
1899 | struct xt_table_info *private = table->private; | ||
1900 | void __user *pos; | ||
1901 | unsigned int size; | ||
1902 | int ret = 0; | ||
1903 | void *loc_cpu_entry; | ||
1904 | unsigned int i = 0; | ||
1905 | |||
1906 | counters = alloc_counters(table); | ||
1907 | if (IS_ERR(counters)) | ||
1908 | return PTR_ERR(counters); | ||
1909 | |||
1910 | /* choose the copy that is on our node/cpu, ... | ||
1911 | * This choice is lazy (because current thread is | ||
1912 | * allowed to migrate to another cpu) | ||
1913 | */ | ||
1914 | loc_cpu_entry = private->entries[raw_smp_processor_id()]; | ||
1915 | pos = userptr; | ||
1916 | size = total_size; | ||
1917 | ret = IP6T_ENTRY_ITERATE(loc_cpu_entry, total_size, | ||
1918 | compat_copy_entry_to_user, | ||
1919 | &pos, &size, counters, &i); | ||
1920 | |||
1921 | vfree(counters); | ||
1922 | return ret; | ||
1923 | } | ||
1924 | |||
1925 | static int | ||
1926 | compat_get_entries(struct compat_ip6t_get_entries __user *uptr, int *len) | ||
1927 | { | ||
1928 | int ret; | ||
1929 | struct compat_ip6t_get_entries get; | ||
1930 | struct xt_table *t; | ||
1931 | |||
1932 | if (*len < sizeof(get)) { | ||
1933 | duprintf("compat_get_entries: %u < %u\n", | ||
1934 | *len, (unsigned int)sizeof(get)); | ||
1935 | return -EINVAL; | ||
1936 | } | ||
1937 | |||
1938 | if (copy_from_user(&get, uptr, sizeof(get)) != 0) | ||
1939 | return -EFAULT; | ||
1940 | |||
1941 | if (*len != sizeof(struct compat_ip6t_get_entries) + get.size) { | ||
1942 | duprintf("compat_get_entries: %u != %u\n", *len, | ||
1943 | (unsigned int)(sizeof(struct compat_ip6t_get_entries) + | ||
1944 | get.size)); | ||
1945 | return -EINVAL; | ||
1946 | } | ||
1947 | |||
1948 | xt_compat_lock(AF_INET6); | ||
1949 | t = xt_find_table_lock(AF_INET6, get.name); | ||
1950 | if (t && !IS_ERR(t)) { | ||
1951 | struct xt_table_info *private = t->private; | ||
1952 | struct xt_table_info info; | ||
1953 | duprintf("t->private->number = %u\n", | ||
1954 | private->number); | ||
1955 | ret = compat_table_info(private, &info); | ||
1956 | if (!ret && get.size == info.size) { | ||
1957 | ret = compat_copy_entries_to_user(private->size, | ||
1958 | t, uptr->entrytable); | ||
1959 | } else if (!ret) { | ||
1960 | duprintf("compat_get_entries: I've got %u not %u!\n", | ||
1961 | private->size, | ||
1962 | get.size); | ||
1963 | ret = -EINVAL; | ||
1964 | } | ||
1965 | xt_compat_flush_offsets(AF_INET6); | ||
1966 | module_put(t->me); | ||
1967 | xt_table_unlock(t); | ||
1968 | } else | ||
1969 | ret = t ? PTR_ERR(t) : -ENOENT; | ||
1970 | |||
1971 | xt_compat_unlock(AF_INET6); | ||
1972 | return ret; | ||
1973 | } | ||
1974 | |||
1975 | static int do_ip6t_get_ctl(struct sock *, int, void __user *, int *); | ||
1976 | |||
1977 | static int | ||
1978 | compat_do_ip6t_get_ctl(struct sock *sk, int cmd, void __user *user, int *len) | ||
1979 | { | ||
1980 | int ret; | ||
1981 | |||
1982 | if (!capable(CAP_NET_ADMIN)) | ||
1983 | return -EPERM; | ||
1984 | |||
1985 | switch (cmd) { | ||
1986 | case IP6T_SO_GET_INFO: | ||
1987 | ret = get_info(user, len, 1); | ||
1988 | break; | ||
1989 | case IP6T_SO_GET_ENTRIES: | ||
1990 | ret = compat_get_entries(user, len); | ||
1991 | break; | ||
1992 | default: | ||
1993 | ret = do_ip6t_get_ctl(sk, cmd, user, len); | ||
1994 | } | ||
1995 | return ret; | ||
1996 | } | ||
1997 | #endif | ||
1998 | |||
1299 | static int | 1999 | static int |
1300 | do_ip6t_set_ctl(struct sock *sk, int cmd, void __user *user, unsigned int len) | 2000 | do_ip6t_set_ctl(struct sock *sk, int cmd, void __user *user, unsigned int len) |
1301 | { | 2001 | { |
@@ -1310,7 +2010,7 @@ do_ip6t_set_ctl(struct sock *sk, int cmd, void __user *user, unsigned int len) | |||
1310 | break; | 2010 | break; |
1311 | 2011 | ||
1312 | case IP6T_SO_SET_ADD_COUNTERS: | 2012 | case IP6T_SO_SET_ADD_COUNTERS: |
1313 | ret = do_add_counters(user, len); | 2013 | ret = do_add_counters(user, len, 0); |
1314 | break; | 2014 | break; |
1315 | 2015 | ||
1316 | default: | 2016 | default: |
@@ -1331,7 +2031,7 @@ do_ip6t_get_ctl(struct sock *sk, int cmd, void __user *user, int *len) | |||
1331 | 2031 | ||
1332 | switch (cmd) { | 2032 | switch (cmd) { |
1333 | case IP6T_SO_GET_INFO: | 2033 | case IP6T_SO_GET_INFO: |
1334 | ret = get_info(user, len); | 2034 | ret = get_info(user, len, 0); |
1335 | break; | 2035 | break; |
1336 | 2036 | ||
1337 | case IP6T_SO_GET_ENTRIES: | 2037 | case IP6T_SO_GET_ENTRIES: |
@@ -1483,6 +2183,11 @@ static struct xt_target ip6t_standard_target __read_mostly = { | |||
1483 | .name = IP6T_STANDARD_TARGET, | 2183 | .name = IP6T_STANDARD_TARGET, |
1484 | .targetsize = sizeof(int), | 2184 | .targetsize = sizeof(int), |
1485 | .family = AF_INET6, | 2185 | .family = AF_INET6, |
2186 | #ifdef CONFIG_COMPAT | ||
2187 | .compatsize = sizeof(compat_int_t), | ||
2188 | .compat_from_user = compat_standard_from_user, | ||
2189 | .compat_to_user = compat_standard_to_user, | ||
2190 | #endif | ||
1486 | }; | 2191 | }; |
1487 | 2192 | ||
1488 | static struct xt_target ip6t_error_target __read_mostly = { | 2193 | static struct xt_target ip6t_error_target __read_mostly = { |
@@ -1497,9 +2202,15 @@ static struct nf_sockopt_ops ip6t_sockopts = { | |||
1497 | .set_optmin = IP6T_BASE_CTL, | 2202 | .set_optmin = IP6T_BASE_CTL, |
1498 | .set_optmax = IP6T_SO_SET_MAX+1, | 2203 | .set_optmax = IP6T_SO_SET_MAX+1, |
1499 | .set = do_ip6t_set_ctl, | 2204 | .set = do_ip6t_set_ctl, |
2205 | #ifdef CONFIG_COMPAT | ||
2206 | .compat_set = compat_do_ip6t_set_ctl, | ||
2207 | #endif | ||
1500 | .get_optmin = IP6T_BASE_CTL, | 2208 | .get_optmin = IP6T_BASE_CTL, |
1501 | .get_optmax = IP6T_SO_GET_MAX+1, | 2209 | .get_optmax = IP6T_SO_GET_MAX+1, |
1502 | .get = do_ip6t_get_ctl, | 2210 | .get = do_ip6t_get_ctl, |
2211 | #ifdef CONFIG_COMPAT | ||
2212 | .compat_get = compat_do_ip6t_get_ctl, | ||
2213 | #endif | ||
1503 | .owner = THIS_MODULE, | 2214 | .owner = THIS_MODULE, |
1504 | }; | 2215 | }; |
1505 | 2216 | ||