+int xt_check_match(const struct xt_match *match, unsigned short family,
+ unsigned int size, const char *table, unsigned int hook_mask,
+ unsigned short proto, int inv_proto)
+{
+ if (XT_ALIGN(match->matchsize) != size) {
+ printk("%s_tables: %s match: invalid size %Zu != %u\n",
+ xt_prefix[family], match->name,
+ XT_ALIGN(match->matchsize), size);
+ return -EINVAL;
+ }
+ if (match->table && strcmp(match->table, table)) {
+ printk("%s_tables: %s match: only valid in %s table, not %s\n",
+ xt_prefix[family], match->name, match->table, table);
+ return -EINVAL;
+ }
+ if (match->hooks && (hook_mask & ~match->hooks) != 0) {
+ printk("%s_tables: %s match: bad hook_mask %u\n",
+ xt_prefix[family], match->name, hook_mask);
+ return -EINVAL;
+ }
+ if (match->proto && (match->proto != proto || inv_proto)) {
+ printk("%s_tables: %s match: only valid for protocol %u\n",
+ xt_prefix[family], match->name, match->proto);
+ return -EINVAL;
+ }
+ return 0;
+}
+EXPORT_SYMBOL_GPL(xt_check_match);
+
+#ifdef CONFIG_COMPAT
+int xt_compat_match_offset(struct xt_match *match)
+{
+ u_int16_t csize = match->compatsize ? : match->matchsize;
+ return XT_ALIGN(match->matchsize) - COMPAT_XT_ALIGN(csize);
+}
+EXPORT_SYMBOL_GPL(xt_compat_match_offset);
+
+void xt_compat_match_from_user(struct xt_entry_match *m, void **dstptr,
+ int *size)
+{
+ struct xt_match *match = m->u.kernel.match;
+ struct compat_xt_entry_match *cm = (struct compat_xt_entry_match *)m;
+ int pad, off = xt_compat_match_offset(match);
+ u_int16_t msize = cm->u.user.match_size;
+
+ m = *dstptr;
+ memcpy(m, cm, sizeof(*cm));
+ if (match->compat_from_user)
+ match->compat_from_user(m->data, cm->data);
+ else
+ memcpy(m->data, cm->data, msize - sizeof(*cm));
+ pad = XT_ALIGN(match->matchsize) - match->matchsize;
+ if (pad > 0)
+ memset(m->data + match->matchsize, 0, pad);
+
+ msize += off;
+ m->u.user.match_size = msize;
+
+ *size += off;
+ *dstptr += msize;
+}
+EXPORT_SYMBOL_GPL(xt_compat_match_from_user);
+
+int xt_compat_match_to_user(struct xt_entry_match *m, void __user **dstptr,
+ int *size)
+{
+ struct xt_match *match = m->u.kernel.match;
+ struct compat_xt_entry_match __user *cm = *dstptr;
+ int off = xt_compat_match_offset(match);
+ u_int16_t msize = m->u.user.match_size - off;
+
+ if (copy_to_user(cm, m, sizeof(*cm)) ||
+ put_user(msize, &cm->u.user.match_size))
+ return -EFAULT;
+
+ if (match->compat_to_user) {
+ if (match->compat_to_user((void __user *)cm->data, m->data))
+ return -EFAULT;
+ } else {
+ if (copy_to_user(cm->data, m->data, msize - sizeof(*cm)))
+ return -EFAULT;
+ }
+
+ *size -= off;
+ *dstptr += msize;
+ return 0;
+}
+EXPORT_SYMBOL_GPL(xt_compat_match_to_user);
+#endif /* CONFIG_COMPAT */
+
+int xt_check_target(const struct xt_target *target, unsigned short family,
+ unsigned int size, const char *table, unsigned int hook_mask,
+ unsigned short proto, int inv_proto)
+{
+ if (XT_ALIGN(target->targetsize) != size) {
+ printk("%s_tables: %s target: invalid size %Zu != %u\n",
+ xt_prefix[family], target->name,
+ XT_ALIGN(target->targetsize), size);
+ return -EINVAL;
+ }
+ if (target->table && strcmp(target->table, table)) {
+ printk("%s_tables: %s target: only valid in %s table, not %s\n",
+ xt_prefix[family], target->name, target->table, table);
+ return -EINVAL;
+ }
+ if (target->hooks && (hook_mask & ~target->hooks) != 0) {
+ printk("%s_tables: %s target: bad hook_mask %u\n",
+ xt_prefix[family], target->name, hook_mask);
+ return -EINVAL;
+ }
+ if (target->proto && (target->proto != proto || inv_proto)) {
+ printk("%s_tables: %s target: only valid for protocol %u\n",
+ xt_prefix[family], target->name, target->proto);
+ return -EINVAL;
+ }
+ return 0;
+}
+EXPORT_SYMBOL_GPL(xt_check_target);
+
+#ifdef CONFIG_COMPAT
+int xt_compat_target_offset(struct xt_target *target)
+{
+ u_int16_t csize = target->compatsize ? : target->targetsize;
+ return XT_ALIGN(target->targetsize) - COMPAT_XT_ALIGN(csize);
+}
+EXPORT_SYMBOL_GPL(xt_compat_target_offset);
+
+void xt_compat_target_from_user(struct xt_entry_target *t, void **dstptr,
+ int *size)
+{
+ struct xt_target *target = t->u.kernel.target;
+ struct compat_xt_entry_target *ct = (struct compat_xt_entry_target *)t;
+ int pad, off = xt_compat_target_offset(target);
+ u_int16_t tsize = ct->u.user.target_size;
+
+ t = *dstptr;
+ memcpy(t, ct, sizeof(*ct));
+ if (target->compat_from_user)
+ target->compat_from_user(t->data, ct->data);
+ else
+ memcpy(t->data, ct->data, tsize - sizeof(*ct));
+ pad = XT_ALIGN(target->targetsize) - target->targetsize;
+ if (pad > 0)
+ memset(t->data + target->targetsize, 0, pad);
+
+ tsize += off;
+ t->u.user.target_size = tsize;
+
+ *size += off;
+ *dstptr += tsize;
+}
+EXPORT_SYMBOL_GPL(xt_compat_target_from_user);
+
+int xt_compat_target_to_user(struct xt_entry_target *t, void __user **dstptr,
+ int *size)
+{
+ struct xt_target *target = t->u.kernel.target;
+ struct compat_xt_entry_target __user *ct = *dstptr;
+ int off = xt_compat_target_offset(target);
+ u_int16_t tsize = t->u.user.target_size - off;
+
+ if (copy_to_user(ct, t, sizeof(*ct)) ||
+ put_user(tsize, &ct->u.user.target_size))
+ return -EFAULT;
+
+ if (target->compat_to_user) {
+ if (target->compat_to_user((void __user *)ct->data, t->data))
+ return -EFAULT;
+ } else {
+ if (copy_to_user(ct->data, t->data, tsize - sizeof(*ct)))
+ return -EFAULT;
+ }
+
+ *size -= off;
+ *dstptr += tsize;
+ return 0;
+}
+EXPORT_SYMBOL_GPL(xt_compat_target_to_user);
+#endif
+