aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--include/linux/netfilter_ipv6/ip6_tables.h35
-rw-r--r--net/compat.c106
-rw-r--r--net/ipv6/netfilter/ip6_tables.c823
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
332struct 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
343static inline struct ip6t_entry_target *
344compat_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 */
323struct 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
335static 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
416out:
417 return ret;
418}
419
420/*
421 * A struct sock_filter is architecture independent. 319 * A struct sock_filter is architecture independent.
422 */ 320 */
423struct compat_sock_fprog { 321struct 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
1040static int get_info(void __user *user, int *len) 1042#ifdef CONFIG_COMPAT
1043static 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
1052static 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
1061static inline int
1062compat_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
1068static 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
1097static 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
1115static 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
1123static int 1213static int
1124do_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
1284static int
1285do_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
1243static int 1354static int
1244do_add_counters(void __user *user, unsigned int len) 1355do_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
1439struct 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
1451static int
1452compat_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;
1491out:
1492 return ret;
1493}
1494
1495static inline int
1496compat_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
1519static inline int
1520compat_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
1529static inline int
1530compat_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
1544static inline int
1545check_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
1621out:
1622 module_put(t->u.kernel.target->me);
1623release_matches:
1624 IP6T_MATCH_ITERATE(e, compat_release_match, &j);
1625 return ret;
1626}
1627
1628static int
1629compat_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
1667static 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
1690static int
1691translate_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
1800free_newinfo:
1801 xt_free_table_info(newinfo);
1802out:
1803 COMPAT_IP6T_ENTRY_ITERATE(entry0, total_size, compat_release_entry, &j);
1804 return ret;
1805out_unlock:
1806 xt_compat_flush_offsets(AF_INET6);
1807 xt_compat_unlock(AF_INET6);
1808 goto out;
1809}
1810
1811static int
1812compat_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
1862static int
1863compat_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
1888struct compat_ip6t_get_entries {
1889 char name[IP6T_TABLE_MAXNAMELEN];
1890 compat_uint_t size;
1891 struct compat_ip6t_entry entrytable[0];
1892};
1893
1894static int
1895compat_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
1925static int
1926compat_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
1975static int do_ip6t_get_ctl(struct sock *, int, void __user *, int *);
1976
1977static int
1978compat_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
1299static int 1999static int
1300do_ip6t_set_ctl(struct sock *sk, int cmd, void __user *user, unsigned int len) 2000do_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
1488static struct xt_target ip6t_error_target __read_mostly = { 2193static 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