--- a/include/uapi/linux/netfilter_ipv4/ip_tables.h +++ b/include/uapi/linux/netfilter_ipv4/ip_tables.h @@ -87,6 +87,7 @@ struct ipt_ip { #define IPT_F_FRAG 0x01 /* Set if rule is a fragment rule */ #define IPT_F_GOTO 0x02 /* Set if jump is a goto */ #define IPT_F_MASK 0x03 /* All possible flag bits mask. */ +#define IPT_F_NO_DEF_MATCH 0x80 /* Internal: no default match rules present */ /* Values for "inv" field in struct ipt_ip. */ #define IPT_INV_VIA_IN 0x01 /* Invert the sense of IN IFACE. */ --- a/net/ipv4/netfilter/ip_tables.c +++ b/net/ipv4/netfilter/ip_tables.c @@ -82,6 +82,9 @@ ip_packet_match(const struct iphdr *ip, #define FWINV(bool, invflg) ((bool) ^ !!(ipinfo->invflags & (invflg))) + if (ipinfo->flags & IPT_F_NO_DEF_MATCH) + return true; + if (FWINV((ip->saddr&ipinfo->smsk.s_addr) != ipinfo->src.s_addr, IPT_INV_SRCIP) || FWINV((ip->daddr&ipinfo->dmsk.s_addr) != ipinfo->dst.s_addr, @@ -135,6 +138,29 @@ ip_packet_match(const struct iphdr *ip, return true; } +static void +ip_checkdefault(struct ipt_ip *ip) +{ + static const char iface_mask[IFNAMSIZ] = {}; + + if (ip->invflags || ip->flags & IPT_F_FRAG) + return; + + if (memcmp(ip->iniface_mask, iface_mask, IFNAMSIZ) != 0) + return; + + if (memcmp(ip->outiface_mask, iface_mask, IFNAMSIZ) != 0) + return; + + if (ip->smsk.s_addr || ip->dmsk.s_addr) + return; + + if (ip->proto) + return; + + ip->flags |= IPT_F_NO_DEF_MATCH; +} + static bool ip_checkentry(const struct ipt_ip *ip) { @@ -650,6 +676,8 @@ find_check_entry(struct ipt_entry *e, st struct xt_mtchk_param mtpar; struct xt_entry_match *ematch; + ip_checkdefault(&e->ip); + j = 0; memset(&mtpar, 0, sizeof(mtpar)); mtpar.net = net; @@ -943,6 +971,7 @@ copy_entries_to_user(unsigned int total_ const struct xt_table_info *private = table->private; int ret = 0; const void *loc_cpu_entry; + u8 flags; counters = alloc_counters(table); if (IS_ERR(counters)) @@ -974,6 +1003,14 @@ copy_entries_to_user(unsigned int total_ goto free_counters; } + flags = e->ip.flags & IPT_F_MASK; + if (copy_to_user(userptr + off + + offsetof(struct ipt_entry, ip.flags), + &flags, sizeof(flags)) != 0) { + ret = -EFAULT; + goto free_counters; + } + for (i = sizeof(struct ipt_entry); i < e->target_offset; i += m->u.match_size) { @@ -1380,12 +1417,15 @@ compat_copy_entry_to_user(struct ipt_ent compat_uint_t origsize; const struct xt_entry_match *ematch; int ret = 0; + u8 flags = e->ip.flags & IPT_F_MASK; origsize = *size; ce = (struct compat_ipt_entry __user *)*dstptr; if (copy_to_user(ce, e, sizeof(struct ipt_entry)) != 0 || copy_to_user(&ce->counters, &counters[i], - sizeof(counters[i])) != 0) + sizeof(counters[i])) != 0 || + copy_to_user(&ce->ip.flags, &flags, + sizeof(flags)) != 0) return -EFAULT; *dstptr += sizeof(struct compat_ipt_entry);