mirror of
git://git.code.sf.net/p/xtables-addons/xtables-addons
synced 2025-09-06 20:55:13 +02:00
Add xt_length2
xt_length2 provides exact layer-4,-5 and -7 length matching besides the preexisting layer-3 length match.
This commit is contained in:
@@ -19,6 +19,7 @@ obj-${build_fuzzy} += xt_fuzzy.o
|
||||
obj-${build_geoip} += xt_geoip.o
|
||||
obj-${build_ipp2p} += xt_ipp2p.o
|
||||
obj-${build_ipset} += ipset/
|
||||
obj-${build_length2} += xt_length2.o
|
||||
obj-${build_portscan} += xt_portscan.o
|
||||
obj-${build_quota2} += xt_quota2.o
|
||||
|
||||
|
@@ -12,5 +12,6 @@ obj-${build_fuzzy} += libxt_fuzzy.so
|
||||
obj-${build_geoip} += libxt_geoip.so
|
||||
obj-${build_ipp2p} += libxt_ipp2p.so
|
||||
obj-${build_ipset} += ipset/
|
||||
obj-${build_length2} += libxt_length2.so
|
||||
obj-${build_portscan} += libxt_portscan.so
|
||||
obj-${build_quota2} += libxt_quota2.so
|
||||
|
18
extensions/libxt_length.man
Normal file
18
extensions/libxt_length.man
Normal file
@@ -0,0 +1,18 @@
|
||||
This module matches the length of a packet against a specific value or range of
|
||||
values.
|
||||
.TP
|
||||
[\fB!\fR] \fB--length\fR \fIlength\fR[\fB:\fR\fIlength\fR]
|
||||
Match exact length or length range.
|
||||
.TP
|
||||
\fB--layer3\fR
|
||||
Match the layer3 frame size (e.g. IPv4/v6 header plus payload).
|
||||
.TP
|
||||
\fB--layer4\fR
|
||||
Match the layer4 frame size (e.g. TCP/UDP header plus payload).
|
||||
.TP
|
||||
\fB--layer5\fR
|
||||
Match the layer5 frame size (e.g. TCP/UDP payload, often called layer7).
|
||||
.PP
|
||||
If no --layer* option is given, --layer3 is assumed by default. Note that using
|
||||
--layer5 may not match a packet if it is not one of the recognized types
|
||||
(currently TCP, UDP, UDPLite, ICMP, AH and ESP) or which has no 5th layer.
|
173
extensions/libxt_length2.c
Normal file
173
extensions/libxt_length2.c
Normal file
@@ -0,0 +1,173 @@
|
||||
#include <getopt.h>
|
||||
#include <netdb.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <xtables.h>
|
||||
#include "xt_length2.h"
|
||||
|
||||
enum {
|
||||
F_LAYER = 1 << 0,
|
||||
F_LENGTH = 1 << 1,
|
||||
|
||||
XT_LENGTH_LAYER_MASK = XT_LENGTH_LAYER3 | XT_LENGTH_LAYER4 |
|
||||
XT_LENGTH_LAYER5 | XT_LENGTH_LAYER7,
|
||||
};
|
||||
|
||||
static void length_mt_help(void)
|
||||
{
|
||||
printf(
|
||||
"length match options:\n"
|
||||
" --layer3 Match against layer3 size (e.g. L4 + IPv6 header)\n"
|
||||
" --layer4 Match against layer4 size (e.g. L5 + SCTP header)\n"
|
||||
" --layer5 Match against layer5 size (e.g. L7 + chunk headers)\n"
|
||||
" --layer7 Match against layer7 payload (e.g. SCTP payload)\n"
|
||||
"[!] --length n[:n] Match packet length against value or range\n"
|
||||
" of values (inclusive)\n"
|
||||
);
|
||||
}
|
||||
|
||||
static const struct option length_mt_opts[] = {
|
||||
{.name = "layer3", .has_arg = false, .val = '3'},
|
||||
{.name = "layer4", .has_arg = false, .val = '4'},
|
||||
{.name = "layer5", .has_arg = false, .val = '5'},
|
||||
{.name = "layer7", .has_arg = false, .val = '7'},
|
||||
{.name = "length", .has_arg = true, .val = '='},
|
||||
{NULL},
|
||||
};
|
||||
|
||||
static void length_mt_init(struct xt_entry_match *match)
|
||||
{
|
||||
struct xt_length_mtinfo2 *info = (void *)match->data;
|
||||
|
||||
info->flags = XT_LENGTH_LAYER3;
|
||||
}
|
||||
|
||||
static int length_mt_parse(int c, char **argv, int invert, unsigned int *flags,
|
||||
const void *entry, struct xt_entry_match **match)
|
||||
{
|
||||
struct xt_length_mtinfo2 *info = (void *)(*match)->data;
|
||||
unsigned int from, to;
|
||||
char *end;
|
||||
|
||||
switch (c) {
|
||||
case '3': /* --layer3 */
|
||||
param_act(P_ONLY_ONCE, "length", "--layer*", *flags & F_LAYER);
|
||||
info->flags &= ~XT_LENGTH_LAYER_MASK;
|
||||
info->flags |= XT_LENGTH_LAYER3;
|
||||
*flags |= F_LAYER;
|
||||
return true;
|
||||
case '4': /* --layer4 */
|
||||
param_act(P_ONLY_ONCE, "length", "--layer*", *flags & F_LAYER);
|
||||
info->flags &= ~XT_LENGTH_LAYER_MASK;
|
||||
info->flags |= XT_LENGTH_LAYER4;
|
||||
*flags |= F_LAYER;
|
||||
return true;
|
||||
case '5': /* --layer5 */
|
||||
param_act(P_ONLY_ONCE, "length", "--layer*", *flags & F_LAYER);
|
||||
info->flags &= ~XT_LENGTH_LAYER_MASK;
|
||||
info->flags |= XT_LENGTH_LAYER5;
|
||||
*flags |= F_LAYER;
|
||||
return true;
|
||||
case '7': /* --layer7 */
|
||||
param_act(P_ONLY_ONCE, "length", "--layer*", *flags & F_LAYER);
|
||||
info->flags &= ~XT_LENGTH_LAYER_MASK;
|
||||
info->flags |= XT_LENGTH_LAYER7;
|
||||
*flags |= F_LAYER;
|
||||
return true;
|
||||
case '=': /* --length */
|
||||
param_act(P_ONLY_ONCE, "length", "--length", *flags & F_LENGTH);
|
||||
if (invert)
|
||||
info->flags |= XT_LENGTH_INVERT;
|
||||
if (!strtonum(optarg, &end, &from, 0, ~0U))
|
||||
param_act(P_BAD_VALUE, "length", "--length", optarg);
|
||||
to = from;
|
||||
if (*end == ':')
|
||||
if (!strtonum(end + 1, &end, &to, 0, ~0U))
|
||||
param_act(P_BAD_VALUE, "length",
|
||||
"--length", optarg);
|
||||
if (*end != '\0')
|
||||
param_act(P_BAD_VALUE, "length", "--length", optarg);
|
||||
info->min = from;
|
||||
info->max = to;
|
||||
*flags |= F_LENGTH;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static void length_mt_check(unsigned int flags)
|
||||
{
|
||||
if (!(flags & F_LENGTH))
|
||||
exit_error(PARAMETER_PROBLEM,
|
||||
"length: You must specify \"--length\"");
|
||||
if (!(flags & F_LAYER))
|
||||
fprintf(stderr, "iptables: length match: Defaulting to "
|
||||
"--layer3. Consider specifying it explicitly.\n");
|
||||
}
|
||||
|
||||
static void length_mt_print(const void *ip, const struct xt_entry_match *match,
|
||||
int numeric)
|
||||
{
|
||||
const struct xt_length_mtinfo2 *info = (const void *)match->data;
|
||||
|
||||
if (info->flags & XT_LENGTH_LAYER3)
|
||||
printf("layer3 ");
|
||||
else if (info->flags & XT_LENGTH_LAYER4)
|
||||
printf("layer4 ");
|
||||
else if (info->flags & XT_LENGTH_LAYER5)
|
||||
printf("layer5 ");
|
||||
else if (info->flags & XT_LENGTH_LAYER7)
|
||||
printf("layer7 ");
|
||||
printf("length ");
|
||||
if (info->flags & XT_LENGTH_INVERT)
|
||||
printf("! ");
|
||||
if (info->min == info->max)
|
||||
printf("%u ", (unsigned int)info->min);
|
||||
else
|
||||
printf("%u-%u ", (unsigned int)info->min,
|
||||
(unsigned int)info->max);
|
||||
}
|
||||
|
||||
static void length_mt_save(const void *ip, const struct xt_entry_match *match)
|
||||
{
|
||||
const struct xt_length_mtinfo2 *info = (const void *)match->data;
|
||||
|
||||
if (info->flags & XT_LENGTH_LAYER3)
|
||||
printf("--layer3 ");
|
||||
else if (info->flags & XT_LENGTH_LAYER4)
|
||||
printf("--layer4 ");
|
||||
else if (info->flags & XT_LENGTH_LAYER5)
|
||||
printf("--layer5 ");
|
||||
else if (info->flags & XT_LENGTH_LAYER7)
|
||||
printf("--layer7 ");
|
||||
if (info->flags & XT_LENGTH_INVERT)
|
||||
printf("! ");
|
||||
printf("--length ");
|
||||
if (info->min == info->max)
|
||||
printf("%u ", (unsigned int)info->min);
|
||||
else
|
||||
printf("%u:%u ", (unsigned int)info->min,
|
||||
(unsigned int)info->max);
|
||||
}
|
||||
|
||||
static struct xtables_match length2_mt_reg = {
|
||||
.version = XTABLES_VERSION,
|
||||
.name = "length2",
|
||||
.revision = 2,
|
||||
.family = PF_UNSPEC,
|
||||
.size = XT_ALIGN(sizeof(struct xt_length_mtinfo2)),
|
||||
.userspacesize = XT_ALIGN(sizeof(struct xt_length_mtinfo2)),
|
||||
.init = length_mt_init,
|
||||
.help = length_mt_help,
|
||||
.parse = length_mt_parse,
|
||||
.final_check = length_mt_check,
|
||||
.print = length_mt_print,
|
||||
.save = length_mt_save,
|
||||
.extra_opts = length_mt_opts,
|
||||
};
|
||||
|
||||
static void _init(void)
|
||||
{
|
||||
xtables_register_match(&length2_mt_reg);
|
||||
}
|
262
extensions/xt_length2.c
Normal file
262
extensions/xt_length2.c
Normal file
@@ -0,0 +1,262 @@
|
||||
/*
|
||||
* xt_length - Netfilter module to match packet length
|
||||
* Copyright © Jan Engelhardt <jengelh@medozas.de>, 2007 - 2009
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License; either
|
||||
* version 2 of the License, or any later version, as published by the
|
||||
* Free Software Foundation.
|
||||
*/
|
||||
#include <linux/dccp.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <linux/icmp.h>
|
||||
#include <linux/ip.h>
|
||||
#include <linux/ipv6.h>
|
||||
#include <linux/sctp.h>
|
||||
#include <linux/tcp.h>
|
||||
#include <linux/udp.h>
|
||||
#include <net/ip.h>
|
||||
#include <net/ipv6.h>
|
||||
#include <linux/netfilter/x_tables.h>
|
||||
#include <linux/netfilter_ipv6/ip6_tables.h>
|
||||
#include "xt_length2.h"
|
||||
#include "compat_xtables.h"
|
||||
#if defined(CONFIG_IP6_NF_IPTABLES) || defined(CONFIG_IP6_NF_IPTABLES_MODULE)
|
||||
# define WITH_IPV6 1
|
||||
#endif
|
||||
#ifndef NEXTHDR_IPV4
|
||||
# define NEXTHDR_IPV4 4
|
||||
#endif
|
||||
|
||||
MODULE_AUTHOR("Jan Engelhardt <jengelh@medozas.de>");
|
||||
MODULE_DESCRIPTION("Xtables: Packet length (Layer3,4,5) match");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_ALIAS("ipt_length2");
|
||||
MODULE_ALIAS("ip6t_length2");
|
||||
|
||||
static bool
|
||||
xtlength_layer5_tcp(unsigned int *length, const struct sk_buff *skb,
|
||||
unsigned int offset)
|
||||
{
|
||||
const struct tcphdr *tcph;
|
||||
struct tcphdr buf;
|
||||
|
||||
tcph = skb_header_pointer(skb, offset, sizeof(buf), &buf);
|
||||
if (tcph == NULL)
|
||||
return false;
|
||||
|
||||
*length = skb->len - offset;
|
||||
if (*length >= 4 * tcph->doff)
|
||||
*length -= 4 * tcph->doff;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
xtlength_layer5_dccp(unsigned int *length, const struct sk_buff *skb,
|
||||
unsigned int offset)
|
||||
{
|
||||
const struct dccp_hdr *dh;
|
||||
struct dccp_hdr dhbuf;
|
||||
|
||||
dh = skb_header_pointer(skb, offset, sizeof(dhbuf), &dhbuf);
|
||||
if (dh == NULL)
|
||||
return false;
|
||||
|
||||
*length = skb->len - offset;
|
||||
if (*length >= 4 * dh->dccph_doff)
|
||||
*length -= 4 * dh->dccph_doff;
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline bool
|
||||
xtlength_layer5(unsigned int *length, const struct sk_buff *skb,
|
||||
unsigned int prot, unsigned int offset)
|
||||
{
|
||||
switch (prot) {
|
||||
case IPPROTO_TCP:
|
||||
return xtlength_layer5_tcp(length, skb, offset);
|
||||
case IPPROTO_UDP:
|
||||
case IPPROTO_UDPLITE:
|
||||
*length = skb->len - offset - sizeof(struct udphdr);
|
||||
return true;
|
||||
case IPPROTO_SCTP:
|
||||
*length = skb->len - offset - sizeof(struct sctphdr);
|
||||
return true;
|
||||
case IPPROTO_DCCP:
|
||||
return xtlength_layer5_dccp(length, skb, offset);
|
||||
case IPPROTO_ICMP:
|
||||
*length = skb->len - offset - sizeof(struct icmphdr);
|
||||
return true;
|
||||
case IPPROTO_ICMPV6:
|
||||
*length = skb->len - offset -
|
||||
offsetof(struct icmp6hdr, icmp6_dataun);
|
||||
return true;
|
||||
case IPPROTO_AH:
|
||||
*length = skb->len - offset - sizeof(struct ip_auth_hdr);
|
||||
return true;
|
||||
case IPPROTO_ESP:
|
||||
*length = skb->len - offset - sizeof(struct ip_esp_hdr);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool
|
||||
xtlength_layer7_sctp(unsigned int *length, const struct sk_buff *skb,
|
||||
unsigned int offset)
|
||||
{
|
||||
const struct sctp_chunkhdr *ch;
|
||||
struct sctp_chunkhdr chbuf;
|
||||
unsigned int pos;
|
||||
|
||||
*length = 0;
|
||||
for (pos = sizeof(struct sctphdr); pos < skb->len;
|
||||
pos += ntohs(ch->length))
|
||||
{
|
||||
ch = skb_header_pointer(skb, offset + pos,
|
||||
sizeof(chbuf), &chbuf);
|
||||
if (ch == NULL)
|
||||
return false;
|
||||
if (ch->type != SCTP_CID_DATA)
|
||||
continue;
|
||||
*length += ntohs(ch->length);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool xtlength_layer7(unsigned int *length, const struct sk_buff *skb,
|
||||
unsigned int proto, unsigned int offset)
|
||||
{
|
||||
switch (proto) {
|
||||
case IPPROTO_SCTP:
|
||||
return xtlength_layer7_sctp(length, skb, offset);
|
||||
default:
|
||||
return xtlength_layer5(length, skb, proto, offset);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* llayer4_proto - figure out the L4 protocol in an IPv6 packet
|
||||
* @skb: skb pointer
|
||||
* @offset: position at which L4 starts (equal to 'protoff' in IPv4 code)
|
||||
* @hotdrop: hotdrop pointer
|
||||
*
|
||||
* Searches for a recognized L4 header. On success, fills in @offset and
|
||||
* returns the protocol number. If not found, %NEXTHDR_MAX is returned.
|
||||
* On error, @hotdrop is set.
|
||||
*/
|
||||
static unsigned int
|
||||
llayer4_proto(const struct sk_buff *skb, unsigned int *offset, bool *hotdrop)
|
||||
{
|
||||
/*
|
||||
* Do encapsulation first so that %NEXTHDR_TCP does not hit the TCP
|
||||
* part in an IPv6-in-IPv6 encapsulation.
|
||||
*/
|
||||
static const unsigned int types[] =
|
||||
{IPPROTO_IPV6, IPPROTO_IPIP, IPPROTO_ESP, IPPROTO_AH,
|
||||
IPPROTO_ICMP, IPPROTO_TCP, IPPROTO_UDP, IPPROTO_UDPLITE,
|
||||
IPPROTO_SCTP, IPPROTO_DCCP};
|
||||
unsigned int i;
|
||||
int err;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(types); ++i) {
|
||||
err = ipv6_find_hdr(skb, offset, types[i], NULL);
|
||||
if (err >= 0)
|
||||
return types[i];
|
||||
if (err != -ENOENT) {
|
||||
*hotdrop = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return NEXTHDR_MAX;
|
||||
}
|
||||
|
||||
static bool
|
||||
length2_mt(const struct sk_buff *skb, const struct xt_match_param *par)
|
||||
{
|
||||
const struct xt_length_mtinfo2 *info = par->matchinfo;
|
||||
const struct iphdr *iph = ip_hdr(skb);
|
||||
unsigned int len = 0;
|
||||
bool hit = true;
|
||||
|
||||
if (info->flags & XT_LENGTH_LAYER3)
|
||||
len = ntohs(iph->tot_len);
|
||||
else if (info->flags & XT_LENGTH_LAYER4)
|
||||
len = ntohs(iph->tot_len) - par->thoff;
|
||||
else if (info->flags & XT_LENGTH_LAYER5)
|
||||
hit = xtlength_layer5(&len, skb, iph->protocol, par->thoff);
|
||||
else if (info->flags & XT_LENGTH_LAYER7)
|
||||
hit = xtlength_layer7(&len, skb, iph->protocol, par->thoff);
|
||||
if (!hit)
|
||||
return false;
|
||||
|
||||
return (len >= info->min && len <= info->max) ^
|
||||
!!(info->flags & XT_LENGTH_INVERT);
|
||||
}
|
||||
|
||||
#ifdef WITH_IPV6
|
||||
static bool
|
||||
length2_mt6(const struct sk_buff *skb, const struct xt_match_param *par)
|
||||
{
|
||||
const struct xt_length_mtinfo2 *info = par->matchinfo;
|
||||
const struct ipv6hdr *iph = ipv6_hdr(skb);
|
||||
unsigned int len = 0, l4proto;
|
||||
unsigned int thoff = par->thoff;
|
||||
bool hit = true;
|
||||
|
||||
if (info->flags & XT_LENGTH_LAYER3) {
|
||||
len = sizeof(struct ipv6hdr) + ntohs(iph->payload_len);
|
||||
} else {
|
||||
l4proto = llayer4_proto(skb, &thoff, par->hotdrop);
|
||||
if (l4proto == NEXTHDR_MAX)
|
||||
return false;
|
||||
if (info->flags & XT_LENGTH_LAYER4)
|
||||
len = skb->len - thoff;
|
||||
else if (info->flags & XT_LENGTH_LAYER5)
|
||||
hit = xtlength_layer5(&len, skb, l4proto, thoff);
|
||||
else if (info->flags & XT_LENGTH_LAYER7)
|
||||
hit = xtlength_layer7(&len, skb, l4proto, thoff);
|
||||
}
|
||||
if (!hit)
|
||||
return false;
|
||||
|
||||
return (len >= info->min && len <= info->max) ^
|
||||
!!(info->flags & XT_LENGTH_INVERT);
|
||||
}
|
||||
#endif
|
||||
|
||||
static struct xt_match length2_mt_reg[] __read_mostly = {
|
||||
{
|
||||
.name = "length2",
|
||||
.revision = 2,
|
||||
.family = NFPROTO_IPV4,
|
||||
.match = length2_mt,
|
||||
.matchsize = sizeof(struct xt_length_mtinfo2),
|
||||
.me = THIS_MODULE,
|
||||
},
|
||||
#ifdef WITH_IPV6
|
||||
{
|
||||
.name = "length2",
|
||||
.revision = 2,
|
||||
.family = NFPROTO_IPV6,
|
||||
.match = length2_mt6,
|
||||
.matchsize = sizeof(struct xt_length_mtinfo2),
|
||||
.me = THIS_MODULE,
|
||||
},
|
||||
#endif
|
||||
};
|
||||
|
||||
static int __init length2_mt_init(void)
|
||||
{
|
||||
return xt_register_matches(length2_mt_reg, ARRAY_SIZE(length2_mt_reg));
|
||||
}
|
||||
|
||||
static void __exit length2_mt_exit(void)
|
||||
{
|
||||
xt_unregister_matches(length2_mt_reg, ARRAY_SIZE(length2_mt_reg));
|
||||
}
|
||||
|
||||
module_init(length2_mt_init);
|
||||
module_exit(length2_mt_exit);
|
22
extensions/xt_length2.h
Normal file
22
extensions/xt_length2.h
Normal file
@@ -0,0 +1,22 @@
|
||||
#ifndef _LINUX_NETFILTER_XT_LENGTH2_H
|
||||
#define _LINUX_NETFILTER_XT_LENGTH2_H
|
||||
|
||||
enum {
|
||||
XT_LENGTH_INVERT = 1 << 0,
|
||||
|
||||
/* IP header plus payload */
|
||||
XT_LENGTH_LAYER3 = 1 << 1,
|
||||
/* Strip IP header: */
|
||||
XT_LENGTH_LAYER4 = 1 << 2,
|
||||
/* Strip TCP/UDP/etc. header */
|
||||
XT_LENGTH_LAYER5 = 1 << 3,
|
||||
/* TCP/UDP/SCTP payload */
|
||||
XT_LENGTH_LAYER7 = 1 << 4,
|
||||
};
|
||||
|
||||
struct xt_length_mtinfo2 {
|
||||
u_int32_t min, max;
|
||||
u_int16_t flags;
|
||||
};
|
||||
|
||||
#endif /* _LINUX_NETFILTER_XT_LENGTH2_H */
|
Reference in New Issue
Block a user