diff -Nur --exclude '*.orig' linux-2.6.6-rc1.org/include/linux/netfilter_ipv4/ip_conntrack.h linux-2.6.6-rc1/include/linux/netfilter_ipv4/ip_conntrack.h --- linux-2.6.6-rc1.org/include/linux/netfilter_ipv4/ip_conntrack.h 2004-04-19 10:38:50.000000000 +0200 +++ linux-2.6.6-rc1/include/linux/netfilter_ipv4/ip_conntrack.h 2004-04-19 10:44:16.000000000 +0200 @@ -207,6 +207,10 @@ } nat; #endif /* CONFIG_IP_NF_NAT_NEEDED */ +#if defined(CONFIG_IP_NF_CONNTRACK_MARK) + unsigned long mark; +#endif + }; /* get master conntrack via master expectation */ diff -Nur --exclude '*.orig' linux-2.6.6-rc1.org/include/linux/netfilter_ipv4/ip_conntrack_tuple.h linux-2.6.6-rc1/include/linux/netfilter_ipv4/ip_conntrack_tuple.h --- linux-2.6.6-rc1.org/include/linux/netfilter_ipv4/ip_conntrack_tuple.h 2004-04-15 03:35:20.000000000 +0200 +++ linux-2.6.6-rc1/include/linux/netfilter_ipv4/ip_conntrack_tuple.h 2004-04-19 10:46:18.000000000 +0200 @@ -25,6 +25,9 @@ struct { u_int16_t id; } icmp; + struct { + u_int16_t port; + } sctp; }; /* The manipulable part of the tuple. */ @@ -55,6 +58,9 @@ struct { u_int8_t type, code; } icmp; + struct { + u_int16_t port; + } sctp; } u; /* The protocol. */ diff -Nur --exclude '*.orig' linux-2.6.6-rc1.org/include/linux/netfilter_ipv4/ipt_CONNMARK.h linux-2.6.6-rc1/include/linux/netfilter_ipv4/ipt_CONNMARK.h --- linux-2.6.6-rc1.org/include/linux/netfilter_ipv4/ipt_CONNMARK.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.6-rc1/include/linux/netfilter_ipv4/ipt_CONNMARK.h 2004-04-19 10:44:16.000000000 +0200 @@ -0,0 +1,25 @@ +#ifndef _IPT_CONNMARK_H_target +#define _IPT_CONNMARK_H_target + +/* Copyright (C) 2002,2004 MARA Systems AB + * by Henrik Nordstrom + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +enum { + IPT_CONNMARK_SET = 0, + IPT_CONNMARK_SAVE, + IPT_CONNMARK_RESTORE +}; + +struct ipt_connmark_target_info { + unsigned long mark; + unsigned long mask; + u_int8_t mode; +}; + +#endif /*_IPT_CONNMARK_H_target*/ diff -Nur --exclude '*.orig' linux-2.6.6-rc1.org/include/linux/netfilter_ipv4/ipt_IPMARK.h linux-2.6.6-rc1/include/linux/netfilter_ipv4/ipt_IPMARK.h --- linux-2.6.6-rc1.org/include/linux/netfilter_ipv4/ipt_IPMARK.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.6-rc1/include/linux/netfilter_ipv4/ipt_IPMARK.h 2004-04-19 10:44:26.000000000 +0200 @@ -0,0 +1,13 @@ +#ifndef _IPT_IPMARK_H_target +#define _IPT_IPMARK_H_target + +struct ipt_ipmark_target_info { + unsigned long andmask; + unsigned long ormask; + unsigned int addr; +}; + +#define IPT_IPMARK_SRC 0 +#define IPT_IPMARK_DST 1 + +#endif /*_IPT_IPMARK_H_target*/ diff -Nur --exclude '*.orig' linux-2.6.6-rc1.org/include/linux/netfilter_ipv4/ipt_XOR.h linux-2.6.6-rc1/include/linux/netfilter_ipv4/ipt_XOR.h --- linux-2.6.6-rc1.org/include/linux/netfilter_ipv4/ipt_XOR.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.6-rc1/include/linux/netfilter_ipv4/ipt_XOR.h 2004-04-19 10:44:34.000000000 +0200 @@ -0,0 +1,9 @@ +#ifndef _IPT_XOR_H +#define _IPT_XOR_H + +struct ipt_XOR_info { + char key[30]; + u_int8_t block_size; +}; + +#endif /* _IPT_XOR_H */ diff -Nur --exclude '*.orig' linux-2.6.6-rc1.org/include/linux/netfilter_ipv4/ipt_addrtype.h linux-2.6.6-rc1/include/linux/netfilter_ipv4/ipt_addrtype.h --- linux-2.6.6-rc1.org/include/linux/netfilter_ipv4/ipt_addrtype.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.6-rc1/include/linux/netfilter_ipv4/ipt_addrtype.h 2004-04-19 10:44:36.000000000 +0200 @@ -0,0 +1,11 @@ +#ifndef _IPT_ADDRTYPE_H +#define _IPT_ADDRTYPE_H + +struct ipt_addrtype_info { + u_int16_t source; /* source-type mask */ + u_int16_t dest; /* dest-type mask */ + int invert_source; + int invert_dest; +}; + +#endif diff -Nur --exclude '*.orig' linux-2.6.6-rc1.org/include/linux/netfilter_ipv4/ipt_connmark.h linux-2.6.6-rc1/include/linux/netfilter_ipv4/ipt_connmark.h --- linux-2.6.6-rc1.org/include/linux/netfilter_ipv4/ipt_connmark.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.6-rc1/include/linux/netfilter_ipv4/ipt_connmark.h 2004-04-19 10:44:16.000000000 +0200 @@ -0,0 +1,18 @@ +#ifndef _IPT_CONNMARK_H +#define _IPT_CONNMARK_H + +/* Copyright (C) 2002,2004 MARA Systems AB + * by Henrik Nordstrom + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +struct ipt_connmark_info { + unsigned long mark, mask; + u_int8_t invert; +}; + +#endif /*_IPT_CONNMARK_H*/ diff -Nur --exclude '*.orig' linux-2.6.6-rc1.org/include/linux/netfilter_ipv4/ipt_policy.h linux-2.6.6-rc1/include/linux/netfilter_ipv4/ipt_policy.h --- linux-2.6.6-rc1.org/include/linux/netfilter_ipv4/ipt_policy.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.6-rc1/include/linux/netfilter_ipv4/ipt_policy.h 2004-04-19 10:46:16.000000000 +0200 @@ -0,0 +1,52 @@ +#ifndef _IPT_POLICY_H +#define _IPT_POLICY_H + +#define POLICY_MAX_ELEM 4 + +enum ipt_policy_flags +{ + POLICY_MATCH_IN = 0x1, + POLICY_MATCH_OUT = 0x2, + POLICY_MATCH_NONE = 0x4, + POLICY_MATCH_STRICT = 0x8, +}; + +enum ipt_policy_modes +{ + POLICY_MODE_TRANSPORT, + POLICY_MODE_TUNNEL +}; + +struct ipt_policy_spec +{ + u_int8_t saddr:1, + daddr:1, + proto:1, + mode:1, + spi:1, + reqid:1; +}; + +struct ipt_policy_elem +{ + u_int32_t saddr; + u_int32_t smask; + u_int32_t daddr; + u_int32_t dmask; + u_int32_t spi; + u_int32_t reqid; + u_int8_t proto; + u_int8_t mode; + + struct ipt_policy_spec match; + struct ipt_policy_spec invert; +}; + +struct ipt_policy_info +{ + struct ipt_policy_elem pol[POLICY_MAX_ELEM]; + u_int16_t flags; + u_int16_t len; +}; + +#endif /* _IPT_POLICY_H */ diff -Nur --exclude '*.orig' linux-2.6.6-rc1.org/include/linux/netfilter_ipv4/ipt_string.h linux-2.6.6-rc1/include/linux/netfilter_ipv4/ipt_string.h --- linux-2.6.6-rc1.org/include/linux/netfilter_ipv4/ipt_string.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.6-rc1/include/linux/netfilter_ipv4/ipt_string.h 2004-04-19 10:47:23.000000000 +0200 @@ -0,0 +1,21 @@ +#ifndef _IPT_STRING_H +#define _IPT_STRING_H + +/* *** PERFORMANCE TWEAK *** + * Packet size and search string threshold, + * above which sublinear searches is used. */ +#define IPT_STRING_HAYSTACK_THRESH 100 +#define IPT_STRING_NEEDLE_THRESH 20 + +#define BM_MAX_NLEN 256 +#define BM_MAX_HLEN 1024 + +typedef char *(*proc_ipt_search) (char *, char *, int, int); + +struct ipt_string_info { + char string[BM_MAX_NLEN]; + u_int16_t invert; + u_int16_t len; +}; + +#endif /* _IPT_STRING_H */ diff -Nur --exclude '*.orig' linux-2.6.6-rc1.org/include/net/tcp.h linux-2.6.6-rc1/include/net/tcp.h --- linux-2.6.6-rc1.org/include/net/tcp.h 2004-04-15 03:33:55.000000000 +0200 +++ linux-2.6.6-rc1/include/net/tcp.h 2004-04-19 10:46:08.000000000 +0200 @@ -162,6 +162,7 @@ extern void tcp_bucket_unlock(struct sock *sk); extern int tcp_port_rover; extern struct sock *tcp_v4_lookup_listener(u32 addr, unsigned short hnum, int dif); +extern struct sock *tcp_v4_lookup(u32 saddr, u16 sport, u32 daddr, u16 hnum, int dif); /* These are AF independent. */ static __inline__ int tcp_bhashfn(__u16 lport) diff -Nur --exclude '*.orig' linux-2.6.6-rc1.org/include/net/udp.h linux-2.6.6-rc1/include/net/udp.h --- linux-2.6.6-rc1.org/include/net/udp.h 2004-04-15 03:35:20.000000000 +0200 +++ linux-2.6.6-rc1/include/net/udp.h 2004-04-19 10:46:08.000000000 +0200 @@ -74,6 +74,8 @@ extern int udp_ioctl(struct sock *sk, int cmd, unsigned long arg); extern int udp_disconnect(struct sock *sk, int flags); +extern struct sock *udp_v4_lookup(u32 saddr, u16 sport, u32 daddr, u16 dport, int dif); + DECLARE_SNMP_STAT(struct udp_mib, udp_statistics); #define UDP_INC_STATS(field) SNMP_INC_STATS(udp_statistics, field) #define UDP_INC_STATS_BH(field) SNMP_INC_STATS_BH(udp_statistics, field) diff -Nur --exclude '*.orig' linux-2.6.6-rc1.org/linux-2.6.4/net/ipv4/netfilter/ipt_unclean.c linux-2.6.6-rc1/linux-2.6.4/net/ipv4/netfilter/ipt_unclean.c --- linux-2.6.6-rc1.org/linux-2.6.4/net/ipv4/netfilter/ipt_unclean.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.6-rc1/linux-2.6.4/net/ipv4/netfilter/ipt_unclean.c 2004-04-19 10:47:06.000000000 +0200 @@ -0,0 +1,604 @@ +/* Kernel module to match suspect packets. */ +#include +#include +#include +#include +#include +#include +#include + +#include + +#define limpk(format, args...) \ +do { \ + if (net_ratelimit()) \ + printk("ipt_unclean: %s" format, \ + embedded ? "(embedded packet) " : "" , ## args); \ +} while(0) + +enum icmp_error_status +{ + ICMP_MAY_BE_ERROR, + ICMP_IS_ERROR, + ICMP_NOT_ERROR +}; + +struct icmp_info +{ + size_t min_len, max_len; + enum icmp_error_status err; + u_int8_t min_code, max_code; +}; + +static int +check_ip(struct iphdr *iph, size_t length, int embedded); + +/* ICMP-specific checks. */ +static int +check_icmp(const struct icmphdr *icmph, + u_int16_t datalen, + unsigned int offset, + int more_frags, + int embedded) +{ + static struct icmp_info info[] + = { [ICMP_ECHOREPLY] + = { 8, 65536, ICMP_NOT_ERROR, 0, 0 }, + [ICMP_DEST_UNREACH] + = { 8 + 28, 65536, ICMP_IS_ERROR, 0, 15 }, + [ICMP_SOURCE_QUENCH] + = { 8 + 28, 65536, ICMP_IS_ERROR, 0, 0 }, + [ICMP_REDIRECT] + = { 8 + 28, 65536, ICMP_IS_ERROR, 0, 3 }, + [ICMP_ECHO] + = { 8, 65536, ICMP_NOT_ERROR, 0, 0 }, + /* Router advertisement. */ + [9] + = { 8, 8 + 255 * 8, ICMP_NOT_ERROR, 0, 0 }, + /* Router solicitation. */ + [10] + = { 8, 8, ICMP_NOT_ERROR, 0, 0 }, + [ICMP_TIME_EXCEEDED] + = { 8 + 28, 65536, ICMP_IS_ERROR, 0, 1 }, + [ICMP_PARAMETERPROB] + = { 8 + 28, 65536, ICMP_IS_ERROR, 0, 1 }, + [ICMP_TIMESTAMP] + = { 20, 20, ICMP_NOT_ERROR, 0, 0 }, + [ICMP_TIMESTAMPREPLY] + = { 20, 20, ICMP_NOT_ERROR, 0, 0 }, + [ICMP_INFO_REQUEST] + = { 8, 65536, ICMP_NOT_ERROR, 0, 0 }, + [ICMP_INFO_REPLY] + = { 8, 65536, ICMP_NOT_ERROR, 0, 0 }, + [ICMP_ADDRESS] + = { 12, 12, ICMP_NOT_ERROR, 0, 0 }, + [ICMP_ADDRESSREPLY] + = { 12, 12, ICMP_NOT_ERROR, 0, 0 } }; + + /* Can't do anything if it's a fragment. */ + if (offset) + return 1; + + /* Must cover type and code. */ + if (datalen < 2) { + limpk("ICMP len=%u too short\n", datalen); + return 0; + } + + /* If not embedded. */ + if (!embedded) { + /* Bad checksum? Don't print, just ignore. */ + if (!more_frags + && ip_compute_csum((unsigned char *) icmph, datalen) != 0) + return 0; + + /* CHECK: Truncated ICMP (even if first fragment). */ + if (icmph->type < sizeof(info)/sizeof(struct icmp_info) + && info[icmph->type].min_len != 0 + && datalen < info[icmph->type].min_len) { + limpk("ICMP type %u len %u too short\n", + icmph->type, datalen); + return 0; + } + + /* CHECK: Check within known error ICMPs. */ + if (icmph->type < sizeof(info)/sizeof(struct icmp_info) + && info[icmph->type].err == ICMP_IS_ERROR) { + /* CHECK: Embedded packet must be at least + length of iph + 8 bytes. */ + struct iphdr *inner = (void *)icmph + 8; + + /* datalen > 8 since all ICMP_IS_ERROR types + have min length > 8 */ + if (datalen - 8 < sizeof(struct iphdr)) { + limpk("ICMP error internal way too short\n"); + return 0; + } + if (datalen - 8 < inner->ihl*4 + 8) { + limpk("ICMP error internal too short\n"); + return 0; + } + if (!check_ip(inner, datalen - 8, 1)) + return 0; + } + } else { + /* CHECK: Can't embed ICMP unless known non-error. */ + if (icmph->type >= sizeof(info)/sizeof(struct icmp_info) + || info[icmph->type].err != ICMP_NOT_ERROR) { + limpk("ICMP type %u not embeddable\n", + icmph->type); + return 0; + } + } + + /* CHECK: Invalid ICMP codes. */ + if (icmph->type < sizeof(info)/sizeof(struct icmp_info) + && (icmph->code < info[icmph->type].min_code + || icmph->code > info[icmph->type].max_code)) { + limpk("ICMP type=%u code=%u\n", + icmph->type, icmph->code); + return 0; + } + + /* CHECK: Above maximum length. */ + if (icmph->type < sizeof(info)/sizeof(struct icmp_info) + && info[icmph->type].max_len != 0 + && datalen > info[icmph->type].max_len) { + limpk("ICMP type=%u too long: %u bytes\n", + icmph->type, datalen); + return 0; + } + + switch (icmph->type) { + case ICMP_PARAMETERPROB: { + /* CHECK: Problem param must be within error packet's + * IP header. */ + struct iphdr *iph = (void *)icmph + 8; + u_int32_t arg = ntohl(icmph->un.gateway); + + if (icmph->code == 0) { + /* Code 0 means that upper 8 bits is pointer + to problem. */ + if ((arg >> 24) >= iph->ihl*4) { + limpk("ICMP PARAMETERPROB ptr = %u\n", + ntohl(icmph->un.gateway) >> 24); + return 0; + } + arg &= 0x00FFFFFF; + } + + /* CHECK: Rest must be zero. */ + if (arg) { + limpk("ICMP PARAMETERPROB nonzero arg = %u\n", + arg); + return 0; + } + break; + } + + case ICMP_TIME_EXCEEDED: + case ICMP_SOURCE_QUENCH: + /* CHECK: Unused must be zero. */ + if (icmph->un.gateway != 0) { + limpk("ICMP type=%u unused = %u\n", + icmph->type, ntohl(icmph->un.gateway)); + return 0; + } + break; + } + + return 1; +} + +/* UDP-specific checks. */ +static int +check_udp(const struct iphdr *iph, + const struct udphdr *udph, + u_int16_t datalen, + unsigned int offset, + int more_frags, + int embedded) +{ + /* Can't do anything if it's a fragment. */ + if (offset) + return 1; + + /* CHECK: Must cover UDP header. */ + if (datalen < sizeof(struct udphdr)) { + limpk("UDP len=%u too short\n", datalen); + return 0; + } + + /* Bad checksum? Don't print, just say it's unclean. */ + /* FIXME: SRC ROUTE packets won't match checksum --RR */ + if (!more_frags && !embedded && udph->check + && csum_tcpudp_magic(iph->saddr, iph->daddr, datalen, IPPROTO_UDP, + csum_partial((char *)udph, datalen, 0)) != 0) + return 0; + + /* CHECK: Destination port can't be zero. */ + if (!udph->dest) { + limpk("UDP zero destination port\n"); + return 0; + } + + if (!more_frags) { + if (!embedded) { + /* CHECK: UDP length must match. */ + if (ntohs(udph->len) != datalen) { + limpk("UDP len too short %u vs %u\n", + ntohs(udph->len), datalen); + return 0; + } + } else { + /* CHECK: UDP length be >= this truncated pkt. */ + if (ntohs(udph->len) < datalen) { + limpk("UDP len too long %u vs %u\n", + ntohs(udph->len), datalen); + return 0; + } + } + } else { + /* CHECK: UDP length must be > this frag's length. */ + if (ntohs(udph->len) <= datalen) { + limpk("UDP fragment len too short %u vs %u\n", + ntohs(udph->len), datalen); + return 0; + } + } + + return 1; +} + +#define TH_FIN 0x01 +#define TH_SYN 0x02 +#define TH_RST 0x04 +#define TH_PUSH 0x08 +#define TH_ACK 0x10 +#define TH_URG 0x20 +#define TH_ECE 0x40 +#define TH_CWR 0x80 + +/* table of valid flag combinations - ECE and CWR are always valid */ +static u8 tcp_valid_flags[(TH_FIN|TH_SYN|TH_RST|TH_PUSH|TH_ACK|TH_URG) + 1] = +{ + [TH_SYN] = 1, + [TH_SYN|TH_ACK] = 1, + [TH_RST] = 1, + [TH_RST|TH_ACK] = 1, + [TH_RST|TH_ACK|TH_PUSH] = 1, + [TH_FIN|TH_ACK] = 1, + [TH_ACK] = 1, + [TH_ACK|TH_PUSH] = 1, + [TH_ACK|TH_URG] = 1, + [TH_ACK|TH_URG|TH_PUSH] = 1, + [TH_FIN|TH_ACK|TH_PUSH] = 1, + [TH_FIN|TH_ACK|TH_URG] = 1, + [TH_FIN|TH_ACK|TH_URG|TH_PUSH] = 1 +}; + +/* TCP-specific checks. */ +static int +check_tcp(const struct iphdr *iph, + const struct tcphdr *tcph, + u_int16_t datalen, + unsigned int offset, + int more_frags, + int embedded) +{ + u_int8_t *opt = (u_int8_t *)tcph; + u_int8_t *endhdr = (u_int8_t *)tcph + tcph->doff * 4; + u_int8_t tcpflags; + int end_of_options = 0; + size_t i; + + /* CHECK: Can't have offset=1: used to override TCP syn-checks. */ + /* In fact, this is caught below (offset < 516). */ + + /* Can't do anything if it's a fragment. */ + if (offset) + return 1; + + /* CHECK: Smaller than minimal TCP hdr. */ + if (datalen < sizeof(struct tcphdr)) { + if (!embedded) { + limpk("Packet length %u < TCP header.\n", datalen); + return 0; + } + /* Must have ports available (datalen >= 8), from + check_icmp which set embedded = 1 */ + /* CHECK: TCP ports inside ICMP error */ + if (!tcph->source || !tcph->dest) { + limpk("Zero TCP ports %u/%u.\n", + htons(tcph->source), htons(tcph->dest)); + return 0; + } + return 1; + } + + /* CHECK: Smaller than actual TCP hdr. */ + if (datalen < tcph->doff * 4) { + if (!embedded) { + limpk("Packet length %u < actual TCP header.\n", + datalen); + return 0; + } else + return 1; + } + + /* Bad checksum? Don't print, just say it's unclean. */ + /* FIXME: SRC ROUTE packets won't match checksum --RR */ + if (!more_frags && !embedded + && csum_tcpudp_magic(iph->saddr, iph->daddr, datalen, IPPROTO_TCP, + csum_partial((char *)tcph, datalen, 0)) != 0) + return 0; + + /* CHECK: TCP ports non-zero */ + if (!tcph->source || !tcph->dest) { + limpk("Zero TCP ports %u/%u.\n", + htons(tcph->source), htons(tcph->dest)); + return 0; + } + + /* CHECK: TCP reserved bits zero. */ + if(tcp_flag_word(tcph) & TCP_RESERVED_BITS) { + limpk("TCP reserved bits not zero\n"); + return 0; + } + + /* CHECK: TCP flags. */ + tcpflags = (((u_int8_t *)tcph)[13] & ~(TH_ECE|TH_CWR)); + if (!tcp_valid_flags[tcpflags]) { + limpk("TCP flags bad: %u\n", tcpflags); + return 0; + } + + for (i = sizeof(struct tcphdr); i < tcph->doff * 4; ) { + switch (opt[i]) { + case 0: + end_of_options = 1; + i++; + break; + case 1: + i++; + break; + default: + /* CHECK: options after EOO. */ + if (end_of_options) { + limpk("TCP option %u after end\n", + opt[i]); + return 0; + } + /* CHECK: options at tail. */ + else if (i+1 >= tcph->doff * 4) { + limpk("TCP option %u at tail\n", + opt[i]); + return 0; + } + /* CHECK: zero-length options. */ + else if (opt[i+1] == 0) { + limpk("TCP option %u 0 len\n", + opt[i]); + return 0; + } + /* CHECK: oversize options. */ + else if (&opt[i] + opt[i+1] > endhdr) { + limpk("TCP option %u at %Zu too long\n", + (unsigned int) opt[i], i); + return 0; + } + /* Move to next option */ + i += opt[i+1]; + } + } + + return 1; +} + +/* Returns 1 if ok */ +/* Standard IP checks. */ +static int +check_ip(struct iphdr *iph, size_t length, int embedded) +{ + u_int8_t *opt = (u_int8_t *)iph; + u_int8_t *endhdr = (u_int8_t *)iph + iph->ihl * 4; + int end_of_options = 0; + void *protoh; + size_t datalen; + unsigned int i; + unsigned int offset; + + /* Should only happen for local outgoing raw-socket packets. */ + /* CHECK: length >= ip header. */ + if (length < sizeof(struct iphdr) || length < iph->ihl * 4) { + limpk("Packet length %Zu < IP header.\n", length); + return 0; + } + + offset = ntohs(iph->frag_off) & IP_OFFSET; + protoh = (void *)iph + iph->ihl * 4; + datalen = length - iph->ihl * 4; + + /* CHECK: Embedded fragment. */ + if (embedded && offset) { + limpk("Embedded fragment.\n"); + return 0; + } + + for (i = sizeof(struct iphdr); i < iph->ihl * 4; ) { + switch (opt[i]) { + case 0: + end_of_options = 1; + i++; + break; + case 1: + i++; + break; + default: + /* CHECK: options after EOO. */ + if (end_of_options) { + limpk("IP option %u after end\n", + opt[i]); + return 0; + } + /* CHECK: options at tail. */ + else if (i+1 >= iph->ihl * 4) { + limpk("IP option %u at tail\n", + opt[i]); + return 0; + } + /* CHECK: zero-length or one-length options. */ + else if (opt[i+1] < 2) { + limpk("IP option %u %u len\n", + opt[i], opt[i+1]); + return 0; + } + /* CHECK: oversize options. */ + else if (&opt[i] + opt[i+1] > endhdr) { + limpk("IP option %u at %u too long\n", + opt[i], i); + return 0; + } + /* Move to next option */ + i += opt[i+1]; + } + } + + /* Fragment checks. */ + + /* CHECK: More fragments, but doesn't fill 8-byte boundary. */ + if ((ntohs(iph->frag_off) & IP_MF) + && (ntohs(iph->tot_len) % 8) != 0) { + limpk("Truncated fragment %u long.\n", ntohs(iph->tot_len)); + return 0; + } + + /* CHECK: Oversize fragment a-la Ping of Death. */ + if (offset * 8 + datalen > 65535) { + limpk("Oversize fragment to %u.\n", offset * 8); + return 0; + } + + /* CHECK: DF set and offset or MF set. */ + if ((ntohs(iph->frag_off) & IP_DF) + && (offset || (ntohs(iph->frag_off) & IP_MF))) { + limpk("DF set and offset=%u, MF=%u.\n", + offset, ntohs(iph->frag_off) & IP_MF); + return 0; + } + + /* CHECK: Zero-sized fragments. */ + if ((offset || (ntohs(iph->frag_off) & IP_MF)) + && datalen == 0) { + limpk("Zero size fragment offset=%u\n", offset); + return 0; + } + + /* Note: we can have even middle fragments smaller than this: + consider a large packet passing through a 600MTU then + 576MTU link: this gives a fragment of 24 data bytes. But + everyone packs fragments largest first, hence a fragment + can't START before 576 - MAX_IP_HEADER_LEN. */ + + /* Used to be min-size 576: I recall Alan Cox saying ax25 goes + down to 128 (576 taken from RFC 791: All hosts must be + prepared to accept datagrams of up to 576 octets). Use 128 + here. */ +#define MIN_LIKELY_MTU 128 + /* CHECK: Min size of first frag = 128. */ + if ((ntohs(iph->frag_off) & IP_MF) + && offset == 0 + && ntohs(iph->tot_len) < MIN_LIKELY_MTU) { + limpk("First fragment size %u < %u\n", ntohs(iph->tot_len), + MIN_LIKELY_MTU); + return 0; + } + + /* CHECK: Min offset of frag = 128 - IP hdr len. */ + if (offset && offset * 8 < MIN_LIKELY_MTU - iph->ihl * 4) { + limpk("Fragment starts at %u < %u\n", offset * 8, + MIN_LIKELY_MTU - iph->ihl * 4); + return 0; + } + + /* CHECK: Protocol specification non-zero. */ + if (iph->protocol == 0) { + limpk("Zero protocol\n"); + return 0; + } + + /* CHECK: Do not use what is unused. + * First bit of fragmentation flags should be unused. + * May be used by OS fingerprinting tools. + * 04 Jun 2002, Maciej Soltysiak, solt@dns.toxicfilms.tv + */ + if (ntohs(iph->frag_off)>>15) { + limpk("IP unused bit set\n"); + return 0; + } + + /* Per-protocol checks. */ + switch (iph->protocol) { + case IPPROTO_ICMP: + return check_icmp(protoh, datalen, offset, + (ntohs(iph->frag_off) & IP_MF), + embedded); + + case IPPROTO_UDP: + return check_udp(iph, protoh, datalen, offset, + (ntohs(iph->frag_off) & IP_MF), + embedded); + + case IPPROTO_TCP: + return check_tcp(iph, protoh, datalen, offset, + (ntohs(iph->frag_off) & IP_MF), + embedded); + default: + /* Ignorance is bliss. */ + return 1; + } +} + +static int +match(const struct sk_buff *skb, + const struct net_device *in, + const struct net_device *out, + const void *matchinfo, + int offset, + const void *hdr, + u_int16_t datalen, + int *hotdrop) +{ + return !check_ip(skb->nh.iph, skb->len, 0); +} + +/* Called when user tries to insert an entry of this type. */ +static int +checkentry(const char *tablename, + const struct ipt_ip *ip, + void *matchinfo, + unsigned int matchsize, + unsigned int hook_mask) +{ + if (matchsize != IPT_ALIGN(0)) + return 0; + + return 1; +} + +static struct ipt_match unclean_match += { { NULL, NULL }, "unclean", &match, &checkentry, NULL, THIS_MODULE }; + +static int __init init(void) +{ + return ipt_register_match(&unclean_match); +} + +static void __exit fini(void) +{ + ipt_unregister_match(&unclean_match); +} + +module_init(init); +module_exit(fini); +MODULE_LICENSE("GPL"); diff -Nur --exclude '*.orig' linux-2.6.6-rc1.org/net/ipv4/netfilter/Kconfig linux-2.6.6-rc1/net/ipv4/netfilter/Kconfig --- linux-2.6.6-rc1.org/net/ipv4/netfilter/Kconfig 2004-04-19 10:38:53.000000000 +0200 +++ linux-2.6.6-rc1/net/ipv4/netfilter/Kconfig 2004-04-19 10:47:23.000000000 +0200 @@ -206,6 +206,11 @@ To compile it as a module, choose M here. If unsure, say N. +config IP_NF_MATCH_UNCLEAN + tristate 'unclean match support (EXPERIMENTAL)' + depends on EXPERIMENTAL && IP_NF_IPTABLES + help + config IP_NF_MATCH_TTL tristate "TTL match support" depends on IP_NF_IPTABLES @@ -701,5 +706,56 @@ depends on IP_NF_IPTABLES help +config IP_NF_CONNTRACK_MARK + bool 'Connection mark tracking support' +config IP_NF_TARGET_CONNMARK + tristate 'CONNMARK target support' + depends on IP_NF_MANGLE +config IP_NF_MATCH_CONNMARK + tristate ' Connection mark match support' + depends on IP_NF_IPTABLES + help + +config IP_NF_TARGET_IPMARK + tristate 'IPMARK target support' + depends on IP_NF_MANGLE + help + +config IP_NF_TARGET_TARPIT + tristate 'TARPIT target support' + depends on IP_NF_FILTER + help + +config IP_NF_TARGET_XOR + tristate 'XOR target support' + depends on IP_NF_MANGLE + help + +config IP_NF_MATCH_ADDRTYPE + tristate 'address type match support' + depends on IP_NF_IPTABLES + help + +config IP_NF_MATCH_POLICY + tristate "IPsec policy match support" + depends on IP_NF_IPTABLES && XFRM + help + Policy matching allows you to match packets based on the + IPsec policy that was used during decapsulation/will + be used during encapsulation. + + To compile it as a module, choose M here. If unsure, say N. + help + +config IP_NF_CT_PROTO_SCTP + tristate 'SCTP protocol connection tracking support' + depends on IP_NF_CONNTRACK + help + +config IP_NF_MATCH_STRING + tristate 'String match support' + depends on IP_NF_IPTABLES + help + endmenu diff -Nur --exclude '*.orig' linux-2.6.6-rc1.org/net/ipv4/netfilter/Makefile linux-2.6.6-rc1/net/ipv4/netfilter/Makefile --- linux-2.6.6-rc1.org/net/ipv4/netfilter/Makefile 2004-04-19 10:38:53.000000000 +0200 +++ linux-2.6.6-rc1/net/ipv4/netfilter/Makefile 2004-04-19 10:47:23.000000000 +0200 @@ -19,6 +19,9 @@ # connection tracking obj-$(CONFIG_IP_NF_CONNTRACK) += ip_conntrack.o +# SCTP protocol connection tracking +obj-$(CONFIG_IP_NF_CT_PROTO_SCTP) += ip_conntrack_proto_sctp.o + # connection tracking helpers obj-$(CONFIG_IP_NF_AMANDA) += ip_conntrack_amanda.o obj-$(CONFIG_IP_NF_TFTP) += ip_conntrack_tftp.o @@ -87,14 +90,19 @@ obj-$(CONFIG_IP_NF_MATCH_U32) += ipt_u32.o +obj-$(CONFIG_IP_NF_MATCH_UNCLEAN) += ipt_unclean.o +obj-$(CONFIG_IP_NF_MATCH_STRING) += ipt_string.o obj-$(CONFIG_IP_NF_MATCH_TTL) += ipt_ttl.o obj-$(CONFIG_IP_NF_MATCH_STATE) += ipt_state.o +obj-$(CONFIG_IP_NF_MATCH_CONNMARK) += ipt_connmark.o obj-$(CONFIG_IP_NF_MATCH_CONNLIMIT) += ipt_connlimit.o obj-$(CONFIG_IP_NF_MATCH_CONNTRACK) += ipt_conntrack.o obj-$(CONFIG_IP_NF_MATCH_TCPMSS) += ipt_tcpmss.o +obj-$(CONFIG_IP_NF_MATCH_ADDRTYPE) += ipt_addrtype.o obj-$(CONFIG_IP_NF_MATCH_REALM) += ipt_realm.o obj-$(CONFIG_IP_NF_MATCH_PHYSDEV) += ipt_physdev.o +obj-$(CONFIG_IP_NF_MATCH_POLICY) += ipt_policy.o # targets obj-$(CONFIG_IP_NF_TARGET_REJECT) += ipt_REJECT.o @@ -102,6 +110,8 @@ obj-$(CONFIG_IP_NF_TARGET_ECN) += ipt_ECN.o obj-$(CONFIG_IP_NF_TARGET_DSCP) += ipt_DSCP.o obj-$(CONFIG_IP_NF_TARGET_MARK) += ipt_MARK.o +obj-$(CONFIG_IP_NF_TARGET_TARPIT) += ipt_TARPIT.o +obj-$(CONFIG_IP_NF_TARGET_IPMARK) += ipt_IPMARK.o obj-$(CONFIG_IP_NF_TARGET_IMQ) += ipt_IMQ.o obj-$(CONFIG_IP_NF_TARGET_MASQUERADE) += ipt_MASQUERADE.o obj-$(CONFIG_IP_NF_TARGET_REDIRECT) += ipt_REDIRECT.o @@ -110,6 +120,8 @@ obj-$(CONFIG_IP_NF_TARGET_CLASSIFY) += ipt_CLASSIFY.o obj-$(CONFIG_IP_NF_NAT_SNMP_BASIC) += ip_nat_snmp_basic.o obj-$(CONFIG_IP_NF_TARGET_LOG) += ipt_LOG.o +obj-$(CONFIG_IP_NF_TARGET_XOR) += ipt_XOR.o +obj-$(CONFIG_IP_NF_TARGET_CONNMARK) += ipt_CONNMARK.o obj-$(CONFIG_IP_NF_TARGET_TTL) += ipt_TTL.o obj-$(CONFIG_IP_NF_TARGET_IPV4OPTSSTRIP) += ipt_IPV4OPTSSTRIP.o obj-$(CONFIG_IP_NF_TARGET_ULOG) += ipt_ULOG.o diff -Nur --exclude '*.orig' linux-2.6.6-rc1.org/net/ipv4/netfilter/ip_conntrack_core.c linux-2.6.6-rc1/net/ipv4/netfilter/ip_conntrack_core.c --- linux-2.6.6-rc1.org/net/ipv4/netfilter/ip_conntrack_core.c 2004-04-19 10:38:53.000000000 +0200 +++ linux-2.6.6-rc1/net/ipv4/netfilter/ip_conntrack_core.c 2004-04-19 10:44:16.000000000 +0200 @@ -717,6 +717,9 @@ __set_bit(IPS_EXPECTED_BIT, &conntrack->status); conntrack->master = expected; expected->sibling = conntrack; +#if CONFIG_IP_NF_CONNTRACK_MARK + conntrack->mark = expected->expectant->mark; +#endif LIST_DELETE(&ip_conntrack_expect_list, expected); expected->expectant->expecting--; nf_conntrack_get(&master_ct(conntrack)->infos[0]); diff -Nur --exclude '*.orig' linux-2.6.6-rc1.org/net/ipv4/netfilter/ip_conntrack_proto_sctp.c linux-2.6.6-rc1/net/ipv4/netfilter/ip_conntrack_proto_sctp.c --- linux-2.6.6-rc1.org/net/ipv4/netfilter/ip_conntrack_proto_sctp.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.6-rc1/net/ipv4/netfilter/ip_conntrack_proto_sctp.c 2004-04-19 10:46:18.000000000 +0200 @@ -0,0 +1,529 @@ +/* + * Connection tracking protocol helper module for SCTP. + * + * SCTP is defined in RFC 2960. References to various sections in this code + * are to this RFC. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#if 0 +#define DEBUGP(format, ...) printk(format, ## __VA_ARGS__) +#else +#define DEBUGP(format, args...) +#endif + +/* Protects conntrack->proto.sctp */ +static DECLARE_RWLOCK(sctp_lock); + +/* FIXME: Examine ipfilter's timeouts and conntrack transitions more + closely. They're more complex. --RR + + And so for me for SCTP :D -Kiran */ + +static const char *sctp_conntrack_names[] = { + "NONE", + "CLOSED", + "COOKIE_WAIT", + "COOKIE_ECHOED", + "ESTABLISHED", + "SHUTDOWN_SENT", + "SHUTDOWN_RECD", + "SHUTDOWN_ACK_SENT", +}; + +#define SECS * HZ +#define MINS * 60 SECS +#define HOURS * 60 MINS +#define DAYS * 24 HOURS + +unsigned long ip_ct_sctp_timeout_closed = 10 SECS; +unsigned long ip_ct_sctp_timeout_cookie_wait = 3 SECS; +unsigned long ip_ct_sctp_timeout_cookie_echoed = 3 SECS; +unsigned long ip_ct_sctp_timeout_established = 5 DAYS; +unsigned long ip_ct_sctp_timeout_shutdown_sent = 300 SECS / 1000; +unsigned long ip_ct_sctp_timeout_shutdown_recd = 300 SECS / 1000; +unsigned long ip_ct_sctp_timeout_shutdown_ack_sent = 3 SECS; + +static unsigned long * sctp_timeouts[] += { 0, /* SCTP_CONNTRACK_NONE */ + &ip_ct_sctp_timeout_closed, /* SCTP_CONNTRACK_CLOSED */ + &ip_ct_sctp_timeout_cookie_wait, /* SCTP_CONNTRACK_COOKIE_WAIT */ + &ip_ct_sctp_timeout_cookie_echoed, /* SCTP_CONNTRACK_COOKIE_ECHOED */ + &ip_ct_sctp_timeout_established, /* SCTP_CONNTRACK_ESTABLISHED */ + &ip_ct_sctp_timeout_shutdown_sent, /* SCTP_CONNTRACK_SHUTDOWN_SENT */ + &ip_ct_sctp_timeout_shutdown_recd, /* SCTP_CONNTRACK_SHUTDOWN_RECD */ + &ip_ct_sctp_timeout_shutdown_ack_sent /* SCTP_CONNTRACK_SHUTDOWN_ACK_SENT */ + }; + +#define sNO SCTP_CONNTRACK_NONE +#define sCL SCTP_CONNTRACK_CLOSED +#define sCW SCTP_CONNTRACK_COOKIE_WAIT +#define sCE SCTP_CONNTRACK_COOKIE_ECHOED +#define sES SCTP_CONNTRACK_ESTABLISHED +#define sSS SCTP_CONNTRACK_SHUTDOWN_SENT +#define sSR SCTP_CONNTRACK_SHUTDOWN_RECD +#define sSA SCTP_CONNTRACK_SHUTDOWN_ACK_SENT +#define sIV SCTP_CONNTRACK_MAX + +/* + These are the descriptions of the states: + +NOTE: These state names are tantalizingly similar to the states of an +SCTP endpoint. But the interpretation of the states is a little different, +considering that these are the states of the connection and not of an end +point. Please note the subtleties. -Kiran + +NONE - Nothing so far. +COOKIE WAIT - We have seen an INIT chunk in the original direction, or also + an INIT_ACK chunk in the reply direction. +COOKIE ECHOED - We have seen a COOKIE_ECHO chunk in the original direction. +ESTABLISHED - We have seen a COOKIE_ACK in the reply direction. +SHUTDOWN_SENT - We have seen a SHUTDOWN chunk in the original direction. +SHUTDOWN_RECD - We have seen a SHUTDOWN chunk in the reply directoin. +SHUTDOWN_ACK_SENT - We have seen a SHUTDOWN_ACK chunk in the direction opposite + to that of the SHUTDOWN chunk. +CLOSED - We have seen a SHUTDOWN_COMPLETE chunk in the direction of + the SHUTDOWN chunk. Connection is closed. +*/ + +/* TODO + - I have assumed that the first INIT is in the original direction. + This messes things when an INIT comes in the reply direction in CLOSED + state. + - Check the error type in the reply dir before transitioning from +cookie echoed to closed. + - Sec 5.2.4 of RFC 2960 + - Multi Homing support. +*/ + +/* SCTP conntrack state transitions */ +static enum sctp_conntrack sctp_conntracks[2][9][SCTP_CONNTRACK_MAX] = { + { +/* ORIGINAL */ +/* sNO, sCL, sCW, sCE, sES, sSS, sSR, sSA */ +/* init */ {sCW, sCW, sCW, sCE, sES, sSS, sSR, sSA}, +/* init_ack */ {sCL, sCL, sCW, sCE, sES, sSS, sSR, sSA}, +/* abort */ {sCL, sCL, sCL, sCL, sCL, sCL, sCL, sCL}, +/* shutdown */ {sCL, sCL, sCW, sCE, sSS, sSS, sSR, sSA}, +/* shutdown_ack */ {sSA, sCL, sCW, sCE, sES, sSA, sSA, sSA}, +/* error */ {sCL, sCL, sCW, sCE, sES, sSS, sSR, sSA},/* Cant have Stale cookie*/ +/* cookie_echo */ {sCL, sCL, sCE, sCE, sES, sSS, sSR, sSA},/* 5.2.4 - Big TODO */ +/* cookie_ack */ {sCL, sCL, sCW, sCE, sES, sSS, sSR, sSA},/* Cant come in orig dir */ +/* shutdown_comp*/ {sCL, sCL, sCW, sCE, sES, sSS, sSR, sCL} + }, + { +/* REPLY */ +/* sNO, sCL, sCW, sCE, sES, sSS, sSR, sSA */ +/* init */ {sIV, sCL, sCW, sCE, sES, sSS, sSR, sSA},/* INIT in sCL Big TODO */ +/* init_ack */ {sIV, sCL, sCW, sCE, sES, sSS, sSR, sSA}, +/* abort */ {sIV, sCL, sCL, sCL, sCL, sCL, sCL, sCL}, +/* shutdown */ {sIV, sCL, sCW, sCE, sSR, sSS, sSR, sSA}, +/* shutdown_ack */ {sIV, sCL, sCW, sCE, sES, sSA, sSA, sSA}, +/* error */ {sIV, sCL, sCW, sCL, sES, sSS, sSR, sSA}, +/* cookie_echo */ {sIV, sCL, sCW, sCE, sES, sSS, sSR, sSA},/* Cant come in reply dir */ +/* cookie_ack */ {sIV, sCL, sCW, sES, sES, sSS, sSR, sSA}, +/* shutdown_comp*/ {sIV, sCL, sCW, sCE, sES, sSS, sSR, sCL} + } +}; + +static int sctp_pkt_to_tuple(const struct sk_buff *skb, + unsigned int dataoff, + struct ip_conntrack_tuple *tuple) +{ + sctp_sctphdr_t hdr; + + DEBUGP(__FUNCTION__); + DEBUGP("\n"); + + /* Actually only need first 8 bytes. */ + if (skb_copy_bits(skb, dataoff, &hdr, 8) != 0) + return 0; + + tuple->src.u.sctp.port = hdr.source; + tuple->dst.u.sctp.port = hdr.dest; + + return 1; +} + +static int sctp_invert_tuple(struct ip_conntrack_tuple *tuple, + const struct ip_conntrack_tuple *orig) +{ + DEBUGP(__FUNCTION__); + DEBUGP("\n"); + + tuple->src.u.sctp.port = orig->dst.u.sctp.port; + tuple->dst.u.sctp.port = orig->src.u.sctp.port; + return 1; +} + +/* Print out the per-protocol part of the tuple. */ +static unsigned int sctp_print_tuple(char *buffer, + const struct ip_conntrack_tuple *tuple) +{ + DEBUGP(__FUNCTION__); + DEBUGP("\n"); + + return sprintf(buffer, "sport=%hu dport=%hu ", + ntohs(tuple->src.u.sctp.port), + ntohs(tuple->dst.u.sctp.port)); +} + +/* Print out the private part of the conntrack. */ +static unsigned int sctp_print_conntrack(char *buffer, + const struct ip_conntrack *conntrack) +{ + enum sctp_conntrack state; + + DEBUGP(__FUNCTION__); + DEBUGP("\n"); + + READ_LOCK(&sctp_lock); + state = conntrack->proto.sctp.state; + READ_UNLOCK(&sctp_lock); + + return sprintf(buffer, "%s ", sctp_conntrack_names[state]); +} + +#define for_each_sctp_chunk(skb, sch, offset, count) \ +for (offset = skb->nh.iph->ihl * 4 + sizeof (sctp_sctphdr_t), count = 0; \ + offset < skb->len && !skb_copy_bits(skb, offset, &sch, sizeof(sch)); \ + offset += (htons(sch.length) + 3) & ~3, count++) + +/* Some validity checks to make sure the chunks are fine */ +static int do_basic_checks(struct ip_conntrack *conntrack, + const struct sk_buff *skb, + char *map) +{ + u_int32_t offset, count; + sctp_chunkhdr_t sch; + int flag; + + DEBUGP(__FUNCTION__); + DEBUGP("\n"); + + flag = 0; + + for_each_sctp_chunk (skb, sch, offset, count) { + DEBUGP("Chunk Num: %d Type: %d\n", count, sch.type); + + if (sch.type == SCTP_CID_INIT + || sch.type == SCTP_CID_INIT_ACK + || sch.type == SCTP_CID_SHUTDOWN_COMPLETE) { + flag = 1; + } + + /* Cookie Ack/Echo chunks not the first OR + Init / Init Ack / Shutdown compl chunks not the only chunks */ + if ((sch.type == SCTP_CID_COOKIE_ACK + || sch.type == SCTP_CID_COOKIE_ECHO + || flag) + && count !=0 ) { + DEBUGP("Basic checks failed\n"); + return 1; + } + + if (map) { + set_bit (sch.type, (void *)map); + } + } + + DEBUGP("Basic checks passed\n"); + return 0; +} + +static int new_state(enum ip_conntrack_dir dir, + enum sctp_conntrack cur_state, + int chunk_type) +{ + int i; + + DEBUGP(__FUNCTION__); + DEBUGP("\n"); + + DEBUGP("Chunk type: %d\n", chunk_type); + + switch (chunk_type) { + case SCTP_CID_INIT: + DEBUGP("SCTP_CID_INIT\n"); + i = 0; break; + case SCTP_CID_INIT_ACK: + DEBUGP("SCTP_CID_INIT_ACK\n"); + i = 1; break; + case SCTP_CID_ABORT: + DEBUGP("SCTP_CID_ABORT\n"); + i = 2; break; + case SCTP_CID_SHUTDOWN: + DEBUGP("SCTP_CID_SHUTDOWN\n"); + i = 3; break; + case SCTP_CID_SHUTDOWN_ACK: + DEBUGP("SCTP_CID_SHUTDOWN_ACK\n"); + i = 4; break; + case SCTP_CID_ERROR: + DEBUGP("SCTP_CID_ERROR\n"); + i = 5; break; + case SCTP_CID_COOKIE_ECHO: + DEBUGP("SCTP_CID_COOKIE_ECHO\n"); + i = 6; break; + case SCTP_CID_COOKIE_ACK: + DEBUGP("SCTP_CID_COOKIE_ACK\n"); + i = 7; break; + case SCTP_CID_SHUTDOWN_COMPLETE: + DEBUGP("SCTP_CID_SHUTDOWN_COMPLETE\n"); + i = 8; break; + default: + /* Other chunks like DATA, SACK, HEARTBEAT and + its ACK do not cause a change in state */ + DEBUGP("Unknown chunk type, Will stay in %s\n", + sctp_conntrack_names[cur_state]); + return cur_state; + } + + DEBUGP("dir: %d cur_state: %s chunk_type: %d new_state: %s\n", + dir, sctp_conntrack_names[cur_state], chunk_type, + sctp_conntrack_names[sctp_conntracks[dir][i][cur_state]]); + + return sctp_conntracks[dir][i][cur_state]; +} + +/* Returns verdict for packet, or -1 for invalid. */ +static int sctp_packet(struct ip_conntrack *conntrack, + const struct sk_buff *skb, + enum ip_conntrack_info ctinfo) +{ + enum sctp_conntrack newconntrack, oldsctpstate; + sctp_sctphdr_t sctph; + sctp_chunkhdr_t sch; + u_int32_t offset, count; + char map[256 / sizeof (char)] = {0}; + + DEBUGP(__FUNCTION__); + DEBUGP("\n"); + + if (skb_copy_bits(skb, skb->nh.iph->ihl * 4, &sctph, sizeof(sctph)) != 0) + return -1; + + if (do_basic_checks(conntrack, skb, map) != 0) + return -1; + + /* Check the verification tag (Sec 8.5) */ + if (!test_bit(SCTP_CID_INIT, (void *)map) + && !test_bit(SCTP_CID_SHUTDOWN_COMPLETE, (void *)map) + && !test_bit(SCTP_CID_COOKIE_ECHO, (void *)map) + && !test_bit(SCTP_CID_ABORT, (void *)map) + && !test_bit(SCTP_CID_SHUTDOWN_ACK, (void *)map) + && (sctph.vtag != conntrack->proto.sctp.vtag[CTINFO2DIR(ctinfo)])) { + DEBUGP("Verification tag check failed\n"); + return -1; + } + + oldsctpstate = newconntrack = SCTP_CONNTRACK_MAX; + for_each_sctp_chunk (skb, sch, offset, count) { + WRITE_LOCK(&sctp_lock); + + /* Special cases of Verification tag check (Sec 8.5.1) */ + if (sch.type == SCTP_CID_INIT) { + /* Sec 8.5.1 (A) */ + if (sctph.vtag != 0) { + WRITE_UNLOCK(&sctp_lock); + return -1; + } + } else if (sch.type == SCTP_CID_ABORT) { + /* Sec 8.5.1 (B) */ + if (!(sctph.vtag == conntrack->proto.sctp.vtag[CTINFO2DIR(ctinfo)]) + && !(sctph.vtag == conntrack->proto.sctp.vtag + [1 - CTINFO2DIR(ctinfo)])) { + WRITE_UNLOCK(&sctp_lock); + return -1; + } + } else if (sch.type == SCTP_CID_SHUTDOWN_COMPLETE) { + /* Sec 8.5.1 (C) */ + if (!(sctph.vtag == conntrack->proto.sctp.vtag[CTINFO2DIR(ctinfo)]) + && !(sctph.vtag == conntrack->proto.sctp.vtag + [1 - CTINFO2DIR(ctinfo)] + && (sch.flags & 1))) { + WRITE_UNLOCK(&sctp_lock); + return -1; + } + } else if (sch.type == SCTP_CID_COOKIE_ECHO) { + /* Sec 8.5.1 (D) */ + if (!(sctph.vtag == conntrack->proto.sctp.vtag[CTINFO2DIR(ctinfo)])) { + WRITE_UNLOCK(&sctp_lock); + return -1; + } + } + + oldsctpstate = conntrack->proto.sctp.state; + newconntrack = new_state(CTINFO2DIR(ctinfo), oldsctpstate, sch.type); + + /* Invalid */ + if (newconntrack == SCTP_CONNTRACK_MAX) { + DEBUGP("ip_conntrack_sctp: Invalid dir=%i ctype=%u conntrack=%u\n", + CTINFO2DIR(ctinfo), sch.type, oldsctpstate); + WRITE_UNLOCK(&sctp_lock); + return -1; + } + + /* If it is an INIT or an INIT ACK note down the vtag */ + if (sch.type == SCTP_CID_INIT + || sch.type == SCTP_CID_INIT_ACK) { + sctp_inithdr_t inithdr; + + if (skb_copy_bits(skb, offset + sizeof (sctp_chunkhdr_t), + &inithdr, sizeof(inithdr)) != 0) { + WRITE_UNLOCK(&sctp_lock); + return -1; + } + DEBUGP("Setting vtag %x for dir %d\n", + inithdr.init_tag, CTINFO2DIR(ctinfo)); + conntrack->proto.sctp.vtag[IP_CT_DIR_ORIGINAL] = inithdr.init_tag; + } + + conntrack->proto.sctp.state = newconntrack; + WRITE_UNLOCK(&sctp_lock); + } + + ip_ct_refresh(conntrack, *sctp_timeouts[newconntrack]); + + if (oldsctpstate == SCTP_CONNTRACK_COOKIE_ECHOED + && CTINFO2DIR(ctinfo) == IP_CT_DIR_REPLY + && newconntrack == SCTP_CONNTRACK_ESTABLISHED) { + DEBUGP("Setting assured bit\n"); + set_bit(IPS_ASSURED_BIT, &conntrack->status); + } + + return NF_ACCEPT; +} + +/* Called when a new connection for this protocol found. */ +static int sctp_new(struct ip_conntrack *conntrack, + const struct sk_buff *skb) +{ + enum sctp_conntrack newconntrack; + sctp_sctphdr_t sctph; + sctp_chunkhdr_t sch; + u_int32_t offset, count; + char map[256 / sizeof (char)] = {0}; + + DEBUGP(__FUNCTION__); + DEBUGP("\n"); + + if (skb_copy_bits(skb, skb->nh.iph->ihl * 4, &sctph, sizeof(sctph)) != 0) + return -1; + + if (do_basic_checks(conntrack, skb, map) != 0) + return -1; + + /* If an OOTB packet has any of these chunks discard (Sec 8.4) */ + if ((test_bit (SCTP_CID_ABORT, (void *)map)) + || (test_bit (SCTP_CID_SHUTDOWN_COMPLETE, (void *)map)) + || (test_bit (SCTP_CID_COOKIE_ACK, (void *)map))) { + return -1; + } + + newconntrack = SCTP_CONNTRACK_MAX; + for_each_sctp_chunk (skb, sch, offset, count) { + /* Don't need lock here: this conntrack not in circulation yet */ + newconntrack = new_state (IP_CT_DIR_ORIGINAL, + SCTP_CONNTRACK_NONE, sch.type); + + /* Invalid: delete conntrack */ + if (newconntrack == SCTP_CONNTRACK_MAX) { + DEBUGP("ip_conntrack_sctp: invalid new deleting.\n"); + return 0; + } + + /* Copy the vtag into the state info */ + if (sch.type == SCTP_CID_INIT) { + if (sctph.vtag == 0) { + sctp_inithdr_t inithdr; + + if (skb_copy_bits(skb, offset + sizeof (sctp_chunkhdr_t), + &inithdr, sizeof(inithdr)) != 0) { + return -1; + } + + DEBUGP("Setting vtag %x for new conn\n", + inithdr.init_tag); + + conntrack->proto.sctp.vtag[IP_CT_DIR_REPLY] = + inithdr.init_tag; + } else { + /* Sec 8.5.1 (A) */ + return -1; + } + } + /* If it is a shutdown ack OOTB packet, we expect a return + shutdown complete, otherwise an ABORT Sec 8.4 (5) and (8) */ + else { + DEBUGP("Setting vtag %x for new conn OOTB\n", + sctph.vtag); + conntrack->proto.sctp.vtag[IP_CT_DIR_REPLY] = sctph.vtag; + } + + conntrack->proto.sctp.state = newconntrack; + } + + return 1; +} + +static int sctp_exp_matches_pkt(struct ip_conntrack_expect *exp, + const struct sk_buff *skb) +{ + /* To be implemented */ + return 0; +} + +struct ip_conntrack_protocol ip_conntrack_protocol_sctp = { + .list = { NULL, NULL }, + .proto = IPPROTO_SCTP, + .name = "sctp", + .pkt_to_tuple = sctp_pkt_to_tuple, + .invert_tuple = sctp_invert_tuple, + .print_tuple = sctp_print_tuple, + .print_conntrack = sctp_print_conntrack, + .packet = sctp_packet, + .new = sctp_new, + .destroy = NULL, + .exp_matches_pkt = sctp_exp_matches_pkt, + .me = THIS_MODULE +}; + +int __init init(void) +{ + int ret; + + ret = ip_conntrack_protocol_register(&ip_conntrack_protocol_sctp); + DEBUGP("SCTP conntrack module loading %s\n", + ret ? "failed": "succeeded"); + return ret; +} + +void __exit fini(void) +{ + ip_conntrack_protocol_unregister(&ip_conntrack_protocol_sctp); + DEBUGP("SCTP conntrack module unloaded\n"); +} + +module_init(init); +module_exit(fini); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Kiran Kumar Immidi"); +MODULE_DESCRIPTION("Netfilter connection tracking protocol helper for SCTP"); diff -Nur --exclude '*.orig' linux-2.6.6-rc1.org/net/ipv4/netfilter/ip_conntrack_standalone.c linux-2.6.6-rc1/net/ipv4/netfilter/ip_conntrack_standalone.c --- linux-2.6.6-rc1.org/net/ipv4/netfilter/ip_conntrack_standalone.c 2004-04-19 10:38:51.000000000 +0200 +++ linux-2.6.6-rc1/net/ipv4/netfilter/ip_conntrack_standalone.c 2004-04-19 10:44:16.000000000 +0200 @@ -110,6 +110,9 @@ len += sprintf(buffer + len, "[ASSURED] "); len += sprintf(buffer + len, "use=%u ", atomic_read(&conntrack->ct_general.use)); +#if defined(CONFIG_IP_NF_CONNTRACK_MARK) + len += sprintf(buffer + len, "mark=%ld ", conntrack->mark); +#endif len += sprintf(buffer + len, "\n"); return len; diff -Nur --exclude '*.orig' linux-2.6.6-rc1.org/net/ipv4/netfilter/ip_tables.c linux-2.6.6-rc1/net/ipv4/netfilter/ip_tables.c --- linux-2.6.6-rc1.org/net/ipv4/netfilter/ip_tables.c 2004-04-19 10:38:53.000000000 +0200 +++ linux-2.6.6-rc1/net/ipv4/netfilter/ip_tables.c 2004-04-19 10:45:57.000000000 +0200 @@ -8,6 +8,10 @@ * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * + * 6 Mar 2002 Robert Olsson + * 17 Apr 2003 Chris Wilson + * - mark_source_chains speedup for complex chains + * * 19 Jan 2002 Harald Welte * - increase module usage count as soon as we have rules inside * a table @@ -498,6 +502,9 @@ { unsigned int hook; + /* keep track of where we have been: */ + unsigned char *been = vmalloc(newinfo->size); + /* No recursion; use packet counter to save back ptrs (reset to 0 as we leave), and comefrom to save source hook bitmask */ for (hook = 0; hook < NF_IP_NUMHOOKS; hook++) { @@ -510,6 +517,7 @@ /* Set initial back pointer. */ e->counters.pcnt = pos; + memset(been, 0, newinfo->size); for (;;) { struct ipt_standard_target *t @@ -518,6 +526,7 @@ if (e->comefrom & (1 << NF_IP_NUMHOOKS)) { printk("iptables: loop hook %u pos %u %08X.\n", hook, pos, e->comefrom); + vfree(been); return 0; } e->comefrom @@ -565,10 +574,14 @@ } else { int newpos = t->verdict; - if (strcmp(t->target.u.user.name, + if ( (pos < 0 || pos >= newinfo->size + || !been[pos]) + && strcmp(t->target.u.user.name, IPT_STANDARD_TARGET) == 0 && newpos >= 0) { /* This a jump; chase it. */ + if (pos >= 0 && pos < newinfo->size) + been[pos]++; duprintf("Jump rule %u -> %u\n", pos, newpos); } else { @@ -584,6 +597,7 @@ next: duprintf("Finished chain %u\n", hook); } + vfree(been); return 1; } diff -Nur --exclude '*.orig' linux-2.6.6-rc1.org/net/ipv4/netfilter/ipt_CONNMARK.c linux-2.6.6-rc1/net/ipv4/netfilter/ipt_CONNMARK.c --- linux-2.6.6-rc1.org/net/ipv4/netfilter/ipt_CONNMARK.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.6-rc1/net/ipv4/netfilter/ipt_CONNMARK.c 2004-04-19 10:44:16.000000000 +0200 @@ -0,0 +1,118 @@ +/* This kernel module is used to modify the connection mark values, or + * to optionally restore the skb nfmark from the connection mark + * + * Copyright (C) 2002,2004 MARA Systems AB + * by Henrik Nordstrom + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include +#include +#include +#include + +MODULE_AUTHOR("Henrik Nordstrom "); +MODULE_DESCRIPTION("IP tables CONNMARK matching module"); +MODULE_LICENSE("GPL"); + +#include +#include +#include + +static unsigned int +target(struct sk_buff **pskb, + const struct net_device *in, + const struct net_device *out, + unsigned int hooknum, + const void *targinfo, + void *userinfo) +{ + const struct ipt_connmark_target_info *markinfo = targinfo; + unsigned long diff; + unsigned long nfmark; + unsigned long newmark; + + enum ip_conntrack_info ctinfo; + struct ip_conntrack *ct = ip_conntrack_get((*pskb), &ctinfo); + if (ct) { + switch(markinfo->mode) { + case IPT_CONNMARK_SET: + newmark = (ct->mark & ~markinfo->mask) | markinfo->mark; + if (newmark != ct->mark) + ct->mark = newmark; + break; + case IPT_CONNMARK_SAVE: + newmark = (ct->mark & ~markinfo->mask) | ((*pskb)->nfmark & markinfo->mask); + if (ct->mark != newmark) + ct->mark = newmark; + break; + case IPT_CONNMARK_RESTORE: + nfmark = (*pskb)->nfmark; + diff = (ct->mark ^ nfmark & markinfo->mask); + if (diff != 0) { + (*pskb)->nfmark = nfmark ^ diff; + (*pskb)->nfcache |= NFC_ALTERED; + } + break; + } + } + + return IPT_CONTINUE; +} + +static int +checkentry(const char *tablename, + const struct ipt_entry *e, + void *targinfo, + unsigned int targinfosize, + unsigned int hook_mask) +{ + struct ipt_connmark_target_info *matchinfo = targinfo; + if (targinfosize != IPT_ALIGN(sizeof(struct ipt_connmark_target_info))) { + printk(KERN_WARNING "CONNMARK: targinfosize %u != %Zu\n", + targinfosize, + IPT_ALIGN(sizeof(struct ipt_connmark_target_info))); + return 0; + } + + if (matchinfo->mode == IPT_CONNMARK_RESTORE) { + if (strcmp(tablename, "mangle") != 0) { + printk(KERN_WARNING "CONNMARK: restore can only be called from \"mangle\" table, not \"%s\"\n", tablename); + return 0; + } + } + + return 1; +} + +static struct ipt_target ipt_connmark_reg = { + .name = "CONNMARK", + .target = &target, + .checkentry = &checkentry, + .me = THIS_MODULE +}; + +static int __init init(void) +{ + return ipt_register_target(&ipt_connmark_reg); +} + +static void __exit fini(void) +{ + ipt_unregister_target(&ipt_connmark_reg); +} + +module_init(init); +module_exit(fini); diff -Nur --exclude '*.orig' linux-2.6.6-rc1.org/net/ipv4/netfilter/ipt_IPMARK.c linux-2.6.6-rc1/net/ipv4/netfilter/ipt_IPMARK.c --- linux-2.6.6-rc1.org/net/ipv4/netfilter/ipt_IPMARK.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.6-rc1/net/ipv4/netfilter/ipt_IPMARK.c 2004-04-19 10:44:26.000000000 +0200 @@ -0,0 +1,81 @@ +/* This is a module which is used for setting the NFMARK field of an skb. */ +#include +#include +#include +#include + +#include +#include + +MODULE_AUTHOR("Grzegorz Janoszka "); +MODULE_DESCRIPTION("IP tables IPMARK: mark based on ip address"); +MODULE_LICENSE("GPL"); + +static unsigned int +target(struct sk_buff **pskb, + const struct net_device *in, + const struct net_device *out, + unsigned int hooknum, + const void *targinfo, + void *userinfo) +{ + const struct ipt_ipmark_target_info *ipmarkinfo = targinfo; + struct iphdr *iph = (*pskb)->nh.iph; + unsigned long mark; + + if (ipmarkinfo->addr == IPT_IPMARK_SRC) + mark = (unsigned long) ntohl(iph->saddr); + else + mark = (unsigned long) ntohl(iph->daddr); + + mark &= ipmarkinfo->andmask; + mark |= ipmarkinfo->ormask; + + if ((*pskb)->nfmark != mark) { + (*pskb)->nfmark = mark; + (*pskb)->nfcache |= NFC_ALTERED; + } + return IPT_CONTINUE; +} + +static int +checkentry(const char *tablename, + const struct ipt_entry *e, + void *targinfo, + unsigned int targinfosize, + unsigned int hook_mask) +{ + if (targinfosize != IPT_ALIGN(sizeof(struct ipt_ipmark_target_info))) { + printk(KERN_WARNING "IPMARK: targinfosize %u != %Zu\n", + targinfosize, + IPT_ALIGN(sizeof(struct ipt_ipmark_target_info))); + return 0; + } + + if (strcmp(tablename, "mangle") != 0) { + printk(KERN_WARNING "IPMARK: can only be called from \"mangle\" table, not \"%s\"\n", tablename); + return 0; + } + + return 1; +} + +static struct ipt_target ipt_ipmark_reg = { + .name = "IPMARK", + .target = target, + .checkentry = checkentry, + .me = THIS_MODULE +}; + +static int __init init(void) +{ + return ipt_register_target(&ipt_ipmark_reg); +} + +static void __exit fini(void) +{ + ipt_unregister_target(&ipt_ipmark_reg); +} + +module_init(init); +module_exit(fini); diff -Nur --exclude '*.orig' linux-2.6.6-rc1.org/net/ipv4/netfilter/ipt_TARPIT.c linux-2.6.6-rc1/net/ipv4/netfilter/ipt_TARPIT.c --- linux-2.6.6-rc1.org/net/ipv4/netfilter/ipt_TARPIT.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.6-rc1/net/ipv4/netfilter/ipt_TARPIT.c 2004-04-19 10:44:29.000000000 +0200 @@ -0,0 +1,287 @@ +/* + * Kernel module to capture and hold incoming TCP connections using + * no local per-connection resources. + * + * Based on ipt_REJECT.c and offering functionality similar to + * LaBrea . + * + * Copyright (c) 2002 Aaron Hopkins + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Goal: + * - Allow incoming TCP connections to be established. + * - Passing data should result in the connection being switched to the + * persist state (0 byte window), in which the remote side stops sending + * data and asks to continue every 60 seconds. + * - Attempts to shut down the connection should be ignored completely, so + * the remote side ends up having to time it out. + * + * This means: + * - Reply to TCP SYN,!ACK,!RST,!FIN with SYN-ACK, window 5 bytes + * - Reply to TCP SYN,ACK,!RST,!FIN with RST to prevent spoofing + * - Reply to TCP !SYN,!RST,!FIN with ACK, window 0 bytes, rate-limited + */ + +#include +#include +#include +#include +#include +#include +#include +struct in_device; +#include +#include +#include + +#if 0 +#define DEBUGP printk +#else +#define DEBUGP(format, args...) +#endif + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Aaron Hopkins "); + +/* Stolen from ip_finish_output2 */ +static int ip_direct_send(struct sk_buff *skb) +{ + struct dst_entry *dst = skb->dst; + struct hh_cache *hh = dst->hh; + + if (hh) { + read_lock_bh(&hh->hh_lock); + memcpy(skb->data - 16, hh->hh_data, 16); + read_unlock_bh(&hh->hh_lock); + skb_push(skb, hh->hh_len); + return hh->hh_output(skb); + } else if (dst->neighbour) + return dst->neighbour->output(skb); + + if (net_ratelimit()) + printk(KERN_DEBUG "TARPIT ip_direct_send: no header cache and no neighbor!\n"); + kfree_skb(skb); + return -EINVAL; +} + + +/* Send reply */ +static void tarpit_tcp(struct sk_buff *oskb,struct rtable *ort,int local) +{ + struct sk_buff *nskb; + struct rtable *nrt; + struct tcphdr *otcph, *ntcph; + unsigned int otcplen; + u_int16_t tmp; + + /* A truncated TCP header isn't going to be useful */ + if (oskb->len < (oskb->nh.iph->ihl*4) + sizeof(struct tcphdr)) + return; + + otcph = (struct tcphdr *)((u_int32_t*)oskb->nh.iph + + oskb->nh.iph->ihl); + otcplen = oskb->len - oskb->nh.iph->ihl*4; + + /* No replies for RST or FIN */ + if (otcph->rst || otcph->fin) + return; + + /* No reply to !SYN,!ACK. Rate-limit replies to !SYN,ACKs */ + if (!otcph->syn && (!otcph->ack || !xrlim_allow(&ort->u.dst, 1*HZ))) + return; + + /* Check checksum. */ + if (tcp_v4_check(otcph, otcplen, oskb->nh.iph->saddr, + oskb->nh.iph->daddr, + csum_partial((char *)otcph, otcplen, 0)) != 0) + return; + + /* Copy skb (even if skb is about to be dropped, we can't just + clone it because there may be other things, such as tcpdump, + interested in it) */ + nskb = skb_copy(oskb, GFP_ATOMIC); + if (!nskb) + return; + + /* This packet will not be the same as the other: clear nf fields */ + nf_conntrack_put(nskb->nfct); + nskb->nfct = NULL; + nskb->nfcache = 0; +#ifdef CONFIG_NETFILTER_DEBUG + nskb->nf_debug = 0; +#endif + + ntcph = (struct tcphdr *)((u_int32_t*)nskb->nh.iph + nskb->nh.iph->ihl); + + /* Truncate to length (no data) */ + ntcph->doff = sizeof(struct tcphdr)/4; + skb_trim(nskb, nskb->nh.iph->ihl*4 + sizeof(struct tcphdr)); + nskb->nh.iph->tot_len = htons(nskb->len); + + /* Swap source and dest */ + nskb->nh.iph->daddr = xchg(&nskb->nh.iph->saddr, nskb->nh.iph->daddr); + tmp = ntcph->source; + ntcph->source = ntcph->dest; + ntcph->dest = tmp; + + /* Use supplied sequence number or make a new one */ + ntcph->seq = otcph->ack ? otcph->ack_seq + : htonl(secure_tcp_sequence_number(nskb->nh.iph->saddr, + nskb->nh.iph->daddr, + ntcph->source, + ntcph->dest)); + + /* Our SYN-ACKs must have a >0 window */ + ntcph->window = (otcph->syn && !otcph->ack) ? htons(5) : 0; + + ntcph->urg_ptr = 0; + + /* Reset flags */ + ((u_int8_t *)ntcph)[13] = 0; + + if (otcph->syn && otcph->ack) { + ntcph->rst = 1; + ntcph->ack_seq = 0; + } else { + ntcph->syn = otcph->syn; + ntcph->ack = 1; + ntcph->ack_seq = htonl(ntohl(otcph->seq) + otcph->syn); + } + + /* Adjust TCP checksum */ + ntcph->check = 0; + ntcph->check = tcp_v4_check(ntcph, sizeof(struct tcphdr), + nskb->nh.iph->saddr, + nskb->nh.iph->daddr, + csum_partial((char *)ntcph, + sizeof(struct tcphdr), 0)); + + /* Adjust IP TTL */ + nskb->nh.iph->ttl = sysctl_ip_default_ttl; + + /* Set DF, id = 0 */ + nskb->nh.iph->frag_off = htons(IP_DF); + nskb->nh.iph->id = 0; + + /* Adjust IP checksum */ + nskb->nh.iph->check = 0; + nskb->nh.iph->check = ip_fast_csum((unsigned char *)nskb->nh.iph, + nskb->nh.iph->ihl); + + if (ip_route_output(&nrt, nskb->nh.iph->daddr, + local ? nskb->nh.iph->saddr : 0, + RT_TOS(nskb->nh.iph->tos) | RTO_CONN, + 0) != 0) + goto free_nskb; + + dst_release(nskb->dst); + nskb->dst = &nrt->u.dst; + + /* "Never happens" */ + if (nskb->len > nskb->dst->pmtu) + goto free_nskb; + + ip_direct_send (nskb); + + return; + + free_nskb: + kfree_skb(nskb); +} + + +static unsigned int tarpit(struct sk_buff **pskb, + const struct net_device *in, + const struct net_device *out, + unsigned int hooknum, + const void *targinfo, + void *userinfo) +{ + struct sk_buff *skb = *pskb; + struct rtable *rt = (struct rtable*)skb->dst; + + /* Do we have an input route cache entry? */ + if (!rt) + return NF_DROP; + + /* No replies to physical multicast/broadcast */ + if (skb->pkt_type != PACKET_HOST && skb->pkt_type != PACKET_OTHERHOST) + return NF_DROP; + + /* Now check at the protocol level */ + if (rt->rt_flags&(RTCF_BROADCAST|RTCF_MULTICAST)) + return NF_DROP; + + /* Our naive response construction doesn't deal with IP + options, and probably shouldn't try. */ + if (skb->nh.iph->ihl*4 != sizeof(struct iphdr)) + return NF_DROP; + + /* We aren't interested in fragments */ + if (skb->nh.iph->frag_off & htons(IP_OFFSET)) + return NF_DROP; + + tarpit_tcp(skb,rt,hooknum == NF_IP_LOCAL_IN); + + return NF_DROP; +} + + +static int check(const char *tablename, + const struct ipt_entry *e, + void *targinfo, + unsigned int targinfosize, + unsigned int hook_mask) +{ + /* Only allow these for input/forward packet filtering. */ + if (strcmp(tablename, "filter") != 0) { + DEBUGP("TARPIT: bad table %s'.\n", tablename); + return 0; + } + if ((hook_mask & ~((1 << NF_IP_LOCAL_IN) + | (1 << NF_IP_FORWARD))) != 0) { + DEBUGP("TARPIT: bad hook mask %X\n", hook_mask); + return 0; + } + + /* Must specify that it's a TCP packet */ + if (e->ip.proto != IPPROTO_TCP || (e->ip.invflags & IPT_INV_PROTO)) { + DEBUGP("TARPIT: not valid for non-tcp\n"); + return 0; + } + + return 1; +} + +static struct ipt_target ipt_tarpit_reg = { + .name = "TARPIT", + .target = tarpit, + .checkentry = check, + .me = THIS_MODULE +}; + +static int __init init(void) +{ + return ipt_register_target(&ipt_tarpit_reg); +} + +static void __exit fini(void) +{ + ipt_unregister_target(&ipt_tarpit_reg); +} + +module_init(init); +module_exit(fini); diff -Nur --exclude '*.orig' linux-2.6.6-rc1.org/net/ipv4/netfilter/ipt_XOR.c linux-2.6.6-rc1/net/ipv4/netfilter/ipt_XOR.c --- linux-2.6.6-rc1.org/net/ipv4/netfilter/ipt_XOR.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.6-rc1/net/ipv4/netfilter/ipt_XOR.c 2004-04-19 10:44:34.000000000 +0200 @@ -0,0 +1,117 @@ +/* XOR target for IP tables + * (C) 2000 by Tim Vandermeersch + * Based on ipt_TTL.c + * + * Version 1.0 + * + * This software is distributed under the terms of GNU GPL + */ + +#include +#include +#include +#include +#include + +#include +#include + +MODULE_AUTHOR("Tim Vandermeersch "); +MODULE_DESCRIPTION("IP tables XOR module"); +MODULE_LICENSE("GPL"); + +static unsigned int +ipt_xor_target(struct sk_buff **pskb, + const struct net_device *in, const struct net_device *out, + unsigned int hooknum, const void *targinfo, void *userinfo) +{ + struct ipt_XOR_info *info = (void *) targinfo; + struct iphdr *iph; + struct tcphdr *tcph; + struct udphdr *udph; + int i, j, k; + + if (!skb_ip_make_writable(pskb, (*pskb)->len)) + return NF_DROP; + + iph = (*pskb)->nh.iph; + + if (iph->protocol == IPPROTO_TCP) { + tcph = (struct tcphdr *) ((*pskb)->data + iph->ihl*4); + for (i=0, j=0; i<(ntohs(iph->tot_len) - iph->ihl*4 - tcph->doff*4); ) { + for (k=0; k<=info->block_size; k++) { + (char) (*pskb)->data[ iph->ihl*4 + tcph->doff*4 + i ] ^= + info->key[j]; + i++; + } + j++; + if (info->key[j] == 0x00) + j = 0; + } + } else if (iph->protocol == IPPROTO_UDP) { + udph = (struct udphdr *) ((*pskb)->data + iph->ihl*4); + for (i=0, j=0; i<(ntohs(udph->len)-8); ) { + for (k=0; k<=info->block_size; k++) { + (char) (*pskb)->data[ iph->ihl*4 + sizeof(struct udphdr) + i ] ^= + info->key[j]; + i++; + } + j++; + if (info->key[j] == 0x00) + j = 0; + } + } + + return IPT_CONTINUE; +} + +static int ipt_xor_checkentry(const char *tablename, const struct ipt_entry *e, + void *targinfo, unsigned int targinfosize, + unsigned int hook_mask) +{ + struct ipt_XOR_info *info = targinfo; + + if (targinfosize != IPT_ALIGN(sizeof(struct ipt_XOR_info))) { + printk(KERN_WARNING "XOR: targinfosize %u != %Zu\n", + targinfosize, IPT_ALIGN(sizeof(struct ipt_XOR_info))); + return 0; + } + + if (strcmp(tablename, "mangle")) { + printk(KERN_WARNING "XOR: can only be called from" + "\"mangle\" table, not \"%s\"\n", tablename); + return 0; + } + + if (!strcmp(info->key, "")) { + printk(KERN_WARNING "XOR: You must specify a key"); + return 0; + } + + if (info->block_size == 0) { + printk(KERN_WARNING "XOR: You must specify a block-size"); + return 0; + } + + return 1; +} + +static struct ipt_target ipt_XOR = { + .name = "XOR", + .target = ipt_xor_target, + .checkentry = ipt_xor_checkentry, + .me = THIS_MODULE, +}; + +static int __init init(void) +{ + return ipt_register_target(&ipt_XOR); +} + +static void __exit fini(void) +{ + ipt_unregister_target(&ipt_XOR); +} + +module_init(init); +module_exit(fini); diff -Nur --exclude '*.orig' linux-2.6.6-rc1.org/net/ipv4/netfilter/ipt_addrtype.c linux-2.6.6-rc1/net/ipv4/netfilter/ipt_addrtype.c --- linux-2.6.6-rc1.org/net/ipv4/netfilter/ipt_addrtype.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.6-rc1/net/ipv4/netfilter/ipt_addrtype.c 2004-04-19 10:44:36.000000000 +0200 @@ -0,0 +1,68 @@ +/* + * iptables module to match inet_addr_type() of an ip. + */ + +#include +#include +#include +#include + +#include +#include + +MODULE_LICENSE("GPL"); + +static inline int match_type(u_int32_t addr, u_int16_t mask) +{ + return !!(mask & (1 << inet_addr_type(addr))); +} + +static int match(const struct sk_buff *skb, const struct net_device *in, + const struct net_device *out, const void *matchinfo, + int offset, int *hotdrop) +{ + const struct ipt_addrtype_info *info = matchinfo; + const struct iphdr *iph = skb->nh.iph; + int ret = 1; + + if (info->source) + ret &= match_type(iph->saddr, info->source)^info->invert_source; + if (info->dest) + ret &= match_type(iph->daddr, info->dest)^info->invert_dest; + + return ret; +} + +static int checkentry(const char *tablename, const struct ipt_ip *ip, + void *matchinfo, unsigned int matchsize, + unsigned int hook_mask) +{ + if (matchsize != IPT_ALIGN(sizeof(struct ipt_addrtype_info))) { + printk(KERN_ERR "ipt_addrtype: invalid size (%u != %u)\n.", + matchsize, IPT_ALIGN(sizeof(struct ipt_addrtype_info))); + return 0; + } + + return 1; +} + +static struct ipt_match addrtype_match = { + .name = "addrtype", + .match = match, + .checkentry = checkentry, + .me = THIS_MODULE +}; + +static int __init init(void) +{ + return ipt_register_match(&addrtype_match); +} + +static void __exit fini(void) +{ + ipt_unregister_match(&addrtype_match); + +} + +module_init(init); +module_exit(fini); diff -Nur --exclude '*.orig' linux-2.6.6-rc1.org/net/ipv4/netfilter/ipt_connmark.c linux-2.6.6-rc1/net/ipv4/netfilter/ipt_connmark.c --- linux-2.6.6-rc1.org/net/ipv4/netfilter/ipt_connmark.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.6-rc1/net/ipv4/netfilter/ipt_connmark.c 2004-04-19 10:44:16.000000000 +0200 @@ -0,0 +1,81 @@ +/* This kernel module matches connection mark values set by the + * CONNMARK target + * + * Copyright (C) 2002,2004 MARA Systems AB + * by Henrik Nordstrom + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include + +MODULE_AUTHOR("Henrik Nordstrom "); +MODULE_DESCRIPTION("IP tables connmark match module"); +MODULE_LICENSE("GPL"); + +#include +#include +#include + +static int +match(const struct sk_buff *skb, + const struct net_device *in, + const struct net_device *out, + const void *matchinfo, + int offset, + int *hotdrop) +{ + const struct ipt_connmark_info *info = matchinfo; + enum ip_conntrack_info ctinfo; + struct ip_conntrack *ct = ip_conntrack_get((struct sk_buff *)skb, &ctinfo); + if (!ct) + return 0; + + return ((ct->mark & info->mask) == info->mark) ^ info->invert; +} + +static int +checkentry(const char *tablename, + const struct ipt_ip *ip, + void *matchinfo, + unsigned int matchsize, + unsigned int hook_mask) +{ + if (matchsize != IPT_ALIGN(sizeof(struct ipt_connmark_info))) + return 0; + + return 1; +} + +static struct ipt_match connmark_match = { + .name = "connmark", + .match = &match, + .checkentry = &checkentry, + .me = THIS_MODULE +}; + +static int __init init(void) +{ + return ipt_register_match(&connmark_match); +} + +static void __exit fini(void) +{ + ipt_unregister_match(&connmark_match); +} + +module_init(init); +module_exit(fini); diff -Nur --exclude '*.orig' linux-2.6.6-rc1.org/net/ipv4/netfilter/ipt_owner.c linux-2.6.6-rc1/net/ipv4/netfilter/ipt_owner.c --- linux-2.6.6-rc1.org/net/ipv4/netfilter/ipt_owner.c 2004-04-15 03:35:57.000000000 +0200 +++ linux-2.6.6-rc1/net/ipv4/netfilter/ipt_owner.c 2004-04-19 10:46:08.000000000 +0200 @@ -6,12 +6,19 @@ * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. + * + * 03/26/2003 Patrick McHardy : LOCAL_IN support */ #include #include #include +#include +#include +#include #include +#include +#include #include #include @@ -21,7 +28,7 @@ MODULE_DESCRIPTION("iptables owner match"); static int -match_comm(const struct sk_buff *skb, const char *comm) +match_comm(const struct sock *sk, const char *comm) { struct task_struct *g, *p; struct files_struct *files; @@ -38,7 +45,7 @@ spin_lock(&files->file_lock); for (i=0; i < files->max_fds; i++) { if (fcheck_files(files, i) == - skb->sk->sk_socket->file) { + sk->sk_socket->file) { spin_unlock(&files->file_lock); task_unlock(p); read_unlock(&tasklist_lock); @@ -54,7 +61,7 @@ } static int -match_pid(const struct sk_buff *skb, pid_t pid) +match_pid(const struct sock *sk, pid_t pid) { struct task_struct *p; struct files_struct *files; @@ -70,7 +77,7 @@ spin_lock(&files->file_lock); for (i=0; i < files->max_fds; i++) { if (fcheck_files(files, i) == - skb->sk->sk_socket->file) { + sk->sk_socket->file) { spin_unlock(&files->file_lock); task_unlock(p); read_unlock(&tasklist_lock); @@ -86,10 +93,10 @@ } static int -match_sid(const struct sk_buff *skb, pid_t sid) +match_sid(const struct sock *sk, pid_t sid) { struct task_struct *g, *p; - struct file *file = skb->sk->sk_socket->file; + struct file *file = sk->sk_socket->file; int i, found=0; read_lock(&tasklist_lock); @@ -129,41 +136,71 @@ int *hotdrop) { const struct ipt_owner_info *info = matchinfo; + struct iphdr *iph = skb->nh.iph; + struct sock *sk = NULL; + int ret = 0; + + if (out) { + sk = skb->sk; + } else { + if (iph->protocol == IPPROTO_TCP) { + struct tcphdr *tcph = + (struct tcphdr *)((u_int32_t *)iph + iph->ihl); + sk = tcp_v4_lookup(iph->saddr, tcph->source, + iph->daddr, tcph->dest, + skb->dev->ifindex); + if (sk && sk->sk_state == TCP_TIME_WAIT) { + tcp_tw_put((struct tcp_tw_bucket *)sk); + return ret; + } + } else if (iph->protocol == IPPROTO_UDP) { + struct udphdr *udph = + (struct udphdr *)((u_int32_t *)iph + iph->ihl); + sk = udp_v4_lookup(iph->saddr, udph->source, iph->daddr, + udph->dest, skb->dev->ifindex); + } + } - if (!skb->sk || !skb->sk->sk_socket || !skb->sk->sk_socket->file) - return 0; + if (!sk || !sk->sk_socket || !sk->sk_socket->file) + goto out; if(info->match & IPT_OWNER_UID) { - if ((skb->sk->sk_socket->file->f_uid != info->uid) ^ + if ((sk->sk_socket->file->f_uid != info->uid) ^ !!(info->invert & IPT_OWNER_UID)) - return 0; + goto out; } if(info->match & IPT_OWNER_GID) { - if ((skb->sk->sk_socket->file->f_gid != info->gid) ^ + if ((sk->sk_socket->file->f_gid != info->gid) ^ !!(info->invert & IPT_OWNER_GID)) - return 0; + goto out; } if(info->match & IPT_OWNER_PID) { - if (!match_pid(skb, info->pid) ^ + if (!match_pid(sk, info->pid) ^ !!(info->invert & IPT_OWNER_PID)) - return 0; + goto out; } if(info->match & IPT_OWNER_SID) { - if (!match_sid(skb, info->sid) ^ + if (!match_sid(sk, info->sid) ^ !!(info->invert & IPT_OWNER_SID)) - return 0; + goto out; } if(info->match & IPT_OWNER_COMM) { - if (!match_comm(skb, info->comm) ^ + if (!match_comm(sk, info->comm) ^ !!(info->invert & IPT_OWNER_COMM)) - return 0; + goto out; } - return 1; + ret = 1; + +out: + if (in && sk) + sock_put(sk); + + return ret; } static int @@ -173,11 +210,19 @@ unsigned int matchsize, unsigned int hook_mask) { - if (hook_mask - & ~((1 << NF_IP_LOCAL_OUT) | (1 << NF_IP_POST_ROUTING))) { - printk("ipt_owner: only valid for LOCAL_OUT or POST_ROUTING.\n"); - return 0; - } + if (hook_mask + & ~((1 << NF_IP_LOCAL_OUT) | (1 << NF_IP_POST_ROUTING) | + (1 << NF_IP_LOCAL_IN))) { + printk("ipt_owner: only valid for LOCAL_IN, LOCAL_OUT " + "or POST_ROUTING.\n"); + return 0; + } + + if ((hook_mask & (1 << NF_IP_LOCAL_IN)) + && ip->proto != IPPROTO_TCP && ip->proto != IPPROTO_UDP) { + printk("ipt_owner: only TCP or UDP can be used in LOCAL_IN\n"); + return 0; + } if (matchsize != IPT_ALIGN(sizeof(struct ipt_owner_info))) { printk("Matchsize %u != %Zu\n", matchsize, diff -Nur --exclude '*.orig' linux-2.6.6-rc1.org/net/ipv4/netfilter/ipt_policy.c linux-2.6.6-rc1/net/ipv4/netfilter/ipt_policy.c --- linux-2.6.6-rc1.org/net/ipv4/netfilter/ipt_policy.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.6-rc1/net/ipv4/netfilter/ipt_policy.c 2004-04-19 10:46:16.000000000 +0200 @@ -0,0 +1,176 @@ +/* IP tables module for matching IPsec policy + * + * Copyright (c) 2004 Patrick McHardy, + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +MODULE_AUTHOR("Patrick McHardy "); +MODULE_DESCRIPTION("IPtables IPsec policy matching module"); +MODULE_LICENSE("GPL"); + + +static inline int +match_xfrm_state(struct xfrm_state *x, const struct ipt_policy_elem *e) +{ +#define MISMATCH(x,y) (e->match.x && ((e->x != (y)) ^ e->invert.x)) + + if (MISMATCH(saddr, x->props.saddr.a4 & e->smask) || + MISMATCH(daddr, x->id.daddr.a4 & e->dmask) || + MISMATCH(proto, x->id.proto) || + MISMATCH(mode, x->props.mode) || + MISMATCH(spi, x->id.spi) || + MISMATCH(reqid, x->props.reqid)) + return 0; + return 1; +} + +static int +match_policy_in(const struct sk_buff *skb, const struct ipt_policy_info *info) +{ + const struct ipt_policy_elem *e; + struct sec_path *sp = skb->sp; + int strict = info->flags & POLICY_MATCH_STRICT; + int i, pos; + + if (sp == NULL) + return -1; + if (strict && info->len != sp->len) + return 0; + + for (i = sp->len - 1; i >= 0; i--) { + pos = strict ? i - sp->len + 1 : 0; + if (pos >= info->len) + return 0; + e = &info->pol[pos]; + + if (match_xfrm_state(sp->x[i].xvec, e)) { + if (!strict) + return 1; + } else if (strict) + return 0; + } + + return strict ? 1 : 0; +} + +static int +match_policy_out(const struct sk_buff *skb, const struct ipt_policy_info *info) +{ + const struct ipt_policy_elem *e; + struct dst_entry *dst = skb->dst; + int strict = info->flags & POLICY_MATCH_STRICT; + int i, pos; + + if (dst->xfrm == NULL) + return -1; + + for (i = 0; dst && dst->xfrm; dst = dst->child, i++) { + pos = strict ? i : 0; + if (pos >= info->len) + return 0; + e = &info->pol[pos]; + + if (match_xfrm_state(dst->xfrm, e)) { + if (!strict) + return 1; + } else if (strict) + return 0; + } + + return strict ? 1 : 0; +} + +static int match(const struct sk_buff *skb, + const struct net_device *in, + const struct net_device *out, + const void *matchinfo, int offset, int *hotdrop) +{ + const struct ipt_policy_info *info = matchinfo; + int ret; + + if (info->flags & POLICY_MATCH_IN) + ret = match_policy_in(skb, info); + else + ret = match_policy_out(skb, info); + + if (ret < 0) { + if (info->flags & POLICY_MATCH_NONE) + ret = 1; + else + ret = 0; + } else if (info->flags & POLICY_MATCH_NONE) + ret = 0; + + return ret; +} + +static int checkentry(const char *tablename, const struct ipt_ip *ip, + void *matchinfo, unsigned int matchsize, + unsigned int hook_mask) +{ + struct ipt_policy_info *info = matchinfo; + + if (matchsize != IPT_ALIGN(sizeof(*info))) { + printk(KERN_ERR "ipt_policy: matchsize %u != %u\n", + matchsize, IPT_ALIGN(sizeof(*info))); + return 0; + } + if (!(info->flags & (POLICY_MATCH_IN|POLICY_MATCH_OUT))) { + printk(KERN_ERR "ipt_policy: neither incoming nor " + "outgoing policy selected\n"); + return 0; + } + if (hook_mask & (1 << NF_IP_PRE_ROUTING | 1 << NF_IP_LOCAL_IN) + && info->flags & POLICY_MATCH_OUT) { + printk(KERN_ERR "ipt_policy: output policy not valid in " + "PRE_ROUTING and INPUT\n"); + return 0; + } + if (hook_mask & (1 << NF_IP_POST_ROUTING | 1 << NF_IP_LOCAL_OUT) + && info->flags & POLICY_MATCH_IN) { + printk(KERN_ERR "ipt_policy: input policy not valid in " + "POST_ROUTING and OUTPUT\n"); + return 0; + } + if (info->len > POLICY_MAX_ELEM) { + printk(KERN_ERR "ipt_policy: too many policy elements\n"); + return 0; + } + + return 1; +} + +static struct ipt_match policy_match = +{ + .name = "policy", + .match = match, + .checkentry = checkentry, + .me = THIS_MODULE, +}; + +static int __init init(void) +{ + return ipt_register_match(&policy_match); +} + +static void __exit fini(void) +{ + ipt_unregister_match(&policy_match); +} + +module_init(init); +module_exit(fini); diff -Nur --exclude '*.orig' linux-2.6.6-rc1.org/net/ipv4/netfilter/ipt_string.c linux-2.6.6-rc1/net/ipv4/netfilter/ipt_string.c --- linux-2.6.6-rc1.org/net/ipv4/netfilter/ipt_string.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.6-rc1/net/ipv4/netfilter/ipt_string.c 2004-04-19 10:47:23.000000000 +0200 @@ -0,0 +1,178 @@ +/* Kernel module to match a string into a packet. + * + * Copyright (C) 2000 Emmanuel Roger + * + * ChangeLog + * 19.02.2002: Gianni Tedesco + * Fixed SMP re-entrancy problem using per-cpu data areas + * for the skip/shift tables. + * 02.05.2001: Gianni Tedesco + * Fixed kernel panic, due to overrunning boyer moore string + * tables. Also slightly tweaked heuristic for deciding what + * search algo to use. + * 27.01.2001: Gianni Tedesco + * Implemented Boyer Moore Sublinear search algorithm + * alongside the existing linear search based on memcmp(). + * Also a quick check to decide which method to use on a per + * packet basis. + */ + +#include +#include +#include +#include +#include +#include + +#include +#include + +MODULE_LICENSE("GPL"); + +struct string_per_cpu { + int skip[BM_MAX_HLEN]; + int shift[BM_MAX_HLEN]; + int len[BM_MAX_HLEN]; +}; + +static DEFINE_PER_CPU(struct string_per_cpu, bm_string_data); + +/* Boyer Moore Sublinear string search - VERY FAST */ +char *search_sublinear (char *needle, char *haystack, int needle_len, int haystack_len) +{ + int M1, right_end, sk, sh; + int ended, j, i; + + int *skip, *shift, *len; + + /* use data suitable for this CPU */ + shift=__gewt_cpu_var(bm_string_data).shift; + skip=__gewt_cpu_var(bm_string_data).skip; + len=__gewt_cpu_var(bm_string_data).len; + + /* Setup skip/shift tables */ + M1 = right_end = needle_len-1; + for (i = 0; i < BM_MAX_HLEN; i++) skip[i] = needle_len; + for (i = 0; needle[i]; i++) skip[(int)needle[i]] = M1 - i; + + for (i = 1; i < needle_len; i++) { + for (j = 0; j < needle_len && needle[M1 - j] == needle[M1 - i - j]; j++); + len[i] = j; + } + + shift[0] = 1; + for (i = 1; i < needle_len; i++) shift[i] = needle_len; + for (i = M1; i > 0; i--) shift[len[i]] = i; + ended = 0; + + for (i = 0; i < needle_len; i++) { + if (len[i] == M1 - i) ended = i; + if (ended) shift[i] = ended; + } + + /* Do the search*/ + while (right_end < haystack_len) + { + for (i = 0; i < needle_len && haystack[right_end - i] == needle[M1 - i]; i++); + if (i == needle_len) { + return haystack+(right_end - M1); + } + + sk = skip[(int)haystack[right_end - i]]; + sh = shift[i]; + right_end = max(right_end - i + sk, right_end + sh); + } + + return NULL; +} + +/* Linear string search based on memcmp() */ +char *search_linear (char *needle, char *haystack, int needle_len, int haystack_len) +{ + char *k = haystack + (haystack_len-needle_len); + char *t = haystack; + + while ( t <= k ) { + if (memcmp(t, needle, needle_len) == 0) + return t; + t++; + } + + return NULL; +} + +static int +match(const struct sk_buff *skb, + const struct net_device *in, + const struct net_device *out, + const void *matchinfo, + int offset, + int *hotdrop) +{ + const struct ipt_string_info *info = matchinfo; + struct iphdr *ip = skb->nh.iph; + int hlen, nlen; + char *needle, *haystack; + proc_ipt_search search=search_linear; + + if ( !ip ) return 0; + + /* get lenghts, and validate them */ + nlen=info->len; + hlen=ntohs(ip->tot_len)-(ip->ihl*4); + if ( nlen > hlen ) return 0; + + needle=(char *)&info->string; + haystack=(char *)ip+(ip->ihl*4); + + /* The sublinear search comes in to its own + * on the larger packets */ + if ( (hlen>IPT_STRING_HAYSTACK_THRESH) && + (nlen>IPT_STRING_NEEDLE_THRESH) ) { + if ( hlen < BM_MAX_HLEN ) { + search=search_sublinear; + }else{ + if (net_ratelimit()) + printk(KERN_INFO "ipt_string: Packet too big " + "to attempt sublinear string search " + "(%d bytes)\n", hlen ); + } + } + + return ((search(needle, haystack, nlen, hlen)!=NULL) ^ info->invert); +} + +static int +checkentry(const char *tablename, + const struct ipt_ip *ip, + void *matchinfo, + unsigned int matchsize, + unsigned int hook_mask) +{ + + if (matchsize != IPT_ALIGN(sizeof(struct ipt_string_info))) + return 0; + + return 1; +} + + +static struct ipt_match string_match = { + .name = "string", + .match = &match, + .checkentry = &checkentry, + .me = THIS_MODULE +}; + +static int __init init(void) +{ + return ipt_register_match(&string_match); +} + +static void __exit fini(void) +{ + ipt_unregister_match(&string_match); +} + +module_init(init); +module_exit(fini); diff -Nur --exclude '*.orig' linux-2.6.6-rc1.org/net/ipv4/netfilter/ipt_unclean.c linux-2.6.6-rc1/net/ipv4/netfilter/ipt_unclean.c --- linux-2.6.6-rc1.org/net/ipv4/netfilter/ipt_unclean.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.6-rc1/net/ipv4/netfilter/ipt_unclean.c 2004-04-19 10:47:12.000000000 +0200 @@ -0,0 +1,604 @@ +/* Kernel module to match suspect packets. */ +#include +#include +#include +#include +#include +#include +#include + +#include + +#define limpk(format, args...) \ +do { \ + if (net_ratelimit()) \ + printk("ipt_unclean: %s" format, \ + embedded ? "(embedded packet) " : "" , ## args); \ +} while(0) + +enum icmp_error_status +{ + ICMP_MAY_BE_ERROR, + ICMP_IS_ERROR, + ICMP_NOT_ERROR +}; + +struct icmp_info +{ + size_t min_len, max_len; + enum icmp_error_status err; + u_int8_t min_code, max_code; +}; + +static int +check_ip(struct iphdr *iph, size_t length, int embedded); + +/* ICMP-specific checks. */ +static int +check_icmp(const struct icmphdr *icmph, + u_int16_t datalen, + unsigned int offset, + int more_frags, + int embedded) +{ + static struct icmp_info info[] + = { [ICMP_ECHOREPLY] + = { 8, 65536, ICMP_NOT_ERROR, 0, 0 }, + [ICMP_DEST_UNREACH] + = { 8 + 28, 65536, ICMP_IS_ERROR, 0, 15 }, + [ICMP_SOURCE_QUENCH] + = { 8 + 28, 65536, ICMP_IS_ERROR, 0, 0 }, + [ICMP_REDIRECT] + = { 8 + 28, 65536, ICMP_IS_ERROR, 0, 3 }, + [ICMP_ECHO] + = { 8, 65536, ICMP_NOT_ERROR, 0, 0 }, + /* Router advertisement. */ + [9] + = { 8, 8 + 255 * 8, ICMP_NOT_ERROR, 0, 0 }, + /* Router solicitation. */ + [10] + = { 8, 8, ICMP_NOT_ERROR, 0, 0 }, + [ICMP_TIME_EXCEEDED] + = { 8 + 28, 65536, ICMP_IS_ERROR, 0, 1 }, + [ICMP_PARAMETERPROB] + = { 8 + 28, 65536, ICMP_IS_ERROR, 0, 1 }, + [ICMP_TIMESTAMP] + = { 20, 20, ICMP_NOT_ERROR, 0, 0 }, + [ICMP_TIMESTAMPREPLY] + = { 20, 20, ICMP_NOT_ERROR, 0, 0 }, + [ICMP_INFO_REQUEST] + = { 8, 65536, ICMP_NOT_ERROR, 0, 0 }, + [ICMP_INFO_REPLY] + = { 8, 65536, ICMP_NOT_ERROR, 0, 0 }, + [ICMP_ADDRESS] + = { 12, 12, ICMP_NOT_ERROR, 0, 0 }, + [ICMP_ADDRESSREPLY] + = { 12, 12, ICMP_NOT_ERROR, 0, 0 } }; + + /* Can't do anything if it's a fragment. */ + if (offset) + return 1; + + /* Must cover type and code. */ + if (datalen < 2) { + limpk("ICMP len=%u too short\n", datalen); + return 0; + } + + /* If not embedded. */ + if (!embedded) { + /* Bad checksum? Don't print, just ignore. */ + if (!more_frags + && ip_compute_csum((unsigned char *) icmph, datalen) != 0) + return 0; + + /* CHECK: Truncated ICMP (even if first fragment). */ + if (icmph->type < sizeof(info)/sizeof(struct icmp_info) + && info[icmph->type].min_len != 0 + && datalen < info[icmph->type].min_len) { + limpk("ICMP type %u len %u too short\n", + icmph->type, datalen); + return 0; + } + + /* CHECK: Check within known error ICMPs. */ + if (icmph->type < sizeof(info)/sizeof(struct icmp_info) + && info[icmph->type].err == ICMP_IS_ERROR) { + /* CHECK: Embedded packet must be at least + length of iph + 8 bytes. */ + struct iphdr *inner = (void *)icmph + 8; + + /* datalen > 8 since all ICMP_IS_ERROR types + have min length > 8 */ + if (datalen - 8 < sizeof(struct iphdr)) { + limpk("ICMP error internal way too short\n"); + return 0; + } + if (datalen - 8 < inner->ihl*4 + 8) { + limpk("ICMP error internal too short\n"); + return 0; + } + if (!check_ip(inner, datalen - 8, 1)) + return 0; + } + } else { + /* CHECK: Can't embed ICMP unless known non-error. */ + if (icmph->type >= sizeof(info)/sizeof(struct icmp_info) + || info[icmph->type].err != ICMP_NOT_ERROR) { + limpk("ICMP type %u not embeddable\n", + icmph->type); + return 0; + } + } + + /* CHECK: Invalid ICMP codes. */ + if (icmph->type < sizeof(info)/sizeof(struct icmp_info) + && (icmph->code < info[icmph->type].min_code + || icmph->code > info[icmph->type].max_code)) { + limpk("ICMP type=%u code=%u\n", + icmph->type, icmph->code); + return 0; + } + + /* CHECK: Above maximum length. */ + if (icmph->type < sizeof(info)/sizeof(struct icmp_info) + && info[icmph->type].max_len != 0 + && datalen > info[icmph->type].max_len) { + limpk("ICMP type=%u too long: %u bytes\n", + icmph->type, datalen); + return 0; + } + + switch (icmph->type) { + case ICMP_PARAMETERPROB: { + /* CHECK: Problem param must be within error packet's + * IP header. */ + struct iphdr *iph = (void *)icmph + 8; + u_int32_t arg = ntohl(icmph->un.gateway); + + if (icmph->code == 0) { + /* Code 0 means that upper 8 bits is pointer + to problem. */ + if ((arg >> 24) >= iph->ihl*4) { + limpk("ICMP PARAMETERPROB ptr = %u\n", + ntohl(icmph->un.gateway) >> 24); + return 0; + } + arg &= 0x00FFFFFF; + } + + /* CHECK: Rest must be zero. */ + if (arg) { + limpk("ICMP PARAMETERPROB nonzero arg = %u\n", + arg); + return 0; + } + break; + } + + case ICMP_TIME_EXCEEDED: + case ICMP_SOURCE_QUENCH: + /* CHECK: Unused must be zero. */ + if (icmph->un.gateway != 0) { + limpk("ICMP type=%u unused = %u\n", + icmph->type, ntohl(icmph->un.gateway)); + return 0; + } + break; + } + + return 1; +} + +/* UDP-specific checks. */ +static int +check_udp(const struct iphdr *iph, + const struct udphdr *udph, + u_int16_t datalen, + unsigned int offset, + int more_frags, + int embedded) +{ + /* Can't do anything if it's a fragment. */ + if (offset) + return 1; + + /* CHECK: Must cover UDP header. */ + if (datalen < sizeof(struct udphdr)) { + limpk("UDP len=%u too short\n", datalen); + return 0; + } + + /* Bad checksum? Don't print, just say it's unclean. */ + /* FIXME: SRC ROUTE packets won't match checksum --RR */ + if (!more_frags && !embedded && udph->check + && csum_tcpudp_magic(iph->saddr, iph->daddr, datalen, IPPROTO_UDP, + csum_partial((char *)udph, datalen, 0)) != 0) + return 0; + + /* CHECK: Destination port can't be zero. */ + if (!udph->dest) { + limpk("UDP zero destination port\n"); + return 0; + } + + if (!more_frags) { + if (!embedded) { + /* CHECK: UDP length must match. */ + if (ntohs(udph->len) != datalen) { + limpk("UDP len too short %u vs %u\n", + ntohs(udph->len), datalen); + return 0; + } + } else { + /* CHECK: UDP length be >= this truncated pkt. */ + if (ntohs(udph->len) < datalen) { + limpk("UDP len too long %u vs %u\n", + ntohs(udph->len), datalen); + return 0; + } + } + } else { + /* CHECK: UDP length must be > this frag's length. */ + if (ntohs(udph->len) <= datalen) { + limpk("UDP fragment len too short %u vs %u\n", + ntohs(udph->len), datalen); + return 0; + } + } + + return 1; +} + +#define TH_FIN 0x01 +#define TH_SYN 0x02 +#define TH_RST 0x04 +#define TH_PUSH 0x08 +#define TH_ACK 0x10 +#define TH_URG 0x20 +#define TH_ECE 0x40 +#define TH_CWR 0x80 + +/* table of valid flag combinations - ECE and CWR are always valid */ +static u8 tcp_valid_flags[(TH_FIN|TH_SYN|TH_RST|TH_PUSH|TH_ACK|TH_URG) + 1] = +{ + [TH_SYN] = 1, + [TH_SYN|TH_ACK] = 1, + [TH_RST] = 1, + [TH_RST|TH_ACK] = 1, + [TH_RST|TH_ACK|TH_PUSH] = 1, + [TH_FIN|TH_ACK] = 1, + [TH_ACK] = 1, + [TH_ACK|TH_PUSH] = 1, + [TH_ACK|TH_URG] = 1, + [TH_ACK|TH_URG|TH_PUSH] = 1, + [TH_FIN|TH_ACK|TH_PUSH] = 1, + [TH_FIN|TH_ACK|TH_URG] = 1, + [TH_FIN|TH_ACK|TH_URG|TH_PUSH] = 1 +}; + +/* TCP-specific checks. */ +static int +check_tcp(const struct iphdr *iph, + const struct tcphdr *tcph, + u_int16_t datalen, + unsigned int offset, + int more_frags, + int embedded) +{ + u_int8_t *opt = (u_int8_t *)tcph; + u_int8_t *endhdr = (u_int8_t *)tcph + tcph->doff * 4; + u_int8_t tcpflags; + int end_of_options = 0; + size_t i; + + /* CHECK: Can't have offset=1: used to override TCP syn-checks. */ + /* In fact, this is caught below (offset < 516). */ + + /* Can't do anything if it's a fragment. */ + if (offset) + return 1; + + /* CHECK: Smaller than minimal TCP hdr. */ + if (datalen < sizeof(struct tcphdr)) { + if (!embedded) { + limpk("Packet length %u < TCP header.\n", datalen); + return 0; + } + /* Must have ports available (datalen >= 8), from + check_icmp which set embedded = 1 */ + /* CHECK: TCP ports inside ICMP error */ + if (!tcph->source || !tcph->dest) { + limpk("Zero TCP ports %u/%u.\n", + htons(tcph->source), htons(tcph->dest)); + return 0; + } + return 1; + } + + /* CHECK: Smaller than actual TCP hdr. */ + if (datalen < tcph->doff * 4) { + if (!embedded) { + limpk("Packet length %u < actual TCP header.\n", + datalen); + return 0; + } else + return 1; + } + + /* Bad checksum? Don't print, just say it's unclean. */ + /* FIXME: SRC ROUTE packets won't match checksum --RR */ + if (!more_frags && !embedded + && csum_tcpudp_magic(iph->saddr, iph->daddr, datalen, IPPROTO_TCP, + csum_partial((char *)tcph, datalen, 0)) != 0) + return 0; + + /* CHECK: TCP ports non-zero */ + if (!tcph->source || !tcph->dest) { + limpk("Zero TCP ports %u/%u.\n", + htons(tcph->source), htons(tcph->dest)); + return 0; + } + + /* CHECK: TCP reserved bits zero. */ + if(tcp_flag_word(tcph) & TCP_RESERVED_BITS) { + limpk("TCP reserved bits not zero\n"); + return 0; + } + + /* CHECK: TCP flags. */ + tcpflags = (((u_int8_t *)tcph)[13] & ~(TH_ECE|TH_CWR)); + if (!tcp_valid_flags[tcpflags]) { + limpk("TCP flags bad: %u\n", tcpflags); + return 0; + } + + for (i = sizeof(struct tcphdr); i < tcph->doff * 4; ) { + switch (opt[i]) { + case 0: + end_of_options = 1; + i++; + break; + case 1: + i++; + break; + default: + /* CHECK: options after EOO. */ + if (end_of_options) { + limpk("TCP option %u after end\n", + opt[i]); + return 0; + } + /* CHECK: options at tail. */ + else if (i+1 >= tcph->doff * 4) { + limpk("TCP option %u at tail\n", + opt[i]); + return 0; + } + /* CHECK: zero-length options. */ + else if (opt[i+1] == 0) { + limpk("TCP option %u 0 len\n", + opt[i]); + return 0; + } + /* CHECK: oversize options. */ + else if (&opt[i] + opt[i+1] > endhdr) { + limpk("TCP option %u at %Zu too long\n", + (unsigned int) opt[i], i); + return 0; + } + /* Move to next option */ + i += opt[i+1]; + } + } + + return 1; +} + +/* Returns 1 if ok */ +/* Standard IP checks. */ +static int +check_ip(struct iphdr *iph, size_t length, int embedded) +{ + u_int8_t *opt = (u_int8_t *)iph; + u_int8_t *endhdr = (u_int8_t *)iph + iph->ihl * 4; + int end_of_options = 0; + void *protoh; + size_t datalen; + unsigned int i; + unsigned int offset; + + /* Should only happen for local outgoing raw-socket packets. */ + /* CHECK: length >= ip header. */ + if (length < sizeof(struct iphdr) || length < iph->ihl * 4) { + limpk("Packet length %Zu < IP header.\n", length); + return 0; + } + + offset = ntohs(iph->frag_off) & IP_OFFSET; + protoh = (void *)iph + iph->ihl * 4; + datalen = length - iph->ihl * 4; + + /* CHECK: Embedded fragment. */ + if (embedded && offset) { + limpk("Embedded fragment.\n"); + return 0; + } + + for (i = sizeof(struct iphdr); i < iph->ihl * 4; ) { + switch (opt[i]) { + case 0: + end_of_options = 1; + i++; + break; + case 1: + i++; + break; + default: + /* CHECK: options after EOO. */ + if (end_of_options) { + limpk("IP option %u after end\n", + opt[i]); + return 0; + } + /* CHECK: options at tail. */ + else if (i+1 >= iph->ihl * 4) { + limpk("IP option %u at tail\n", + opt[i]); + return 0; + } + /* CHECK: zero-length or one-length options. */ + else if (opt[i+1] < 2) { + limpk("IP option %u %u len\n", + opt[i], opt[i+1]); + return 0; + } + /* CHECK: oversize options. */ + else if (&opt[i] + opt[i+1] > endhdr) { + limpk("IP option %u at %u too long\n", + opt[i], i); + return 0; + } + /* Move to next option */ + i += opt[i+1]; + } + } + + /* Fragment checks. */ + + /* CHECK: More fragments, but doesn't fill 8-byte boundary. */ + if ((ntohs(iph->frag_off) & IP_MF) + && (ntohs(iph->tot_len) % 8) != 0) { + limpk("Truncated fragment %u long.\n", ntohs(iph->tot_len)); + return 0; + } + + /* CHECK: Oversize fragment a-la Ping of Death. */ + if (offset * 8 + datalen > 65535) { + limpk("Oversize fragment to %u.\n", offset * 8); + return 0; + } + + /* CHECK: DF set and offset or MF set. */ + if ((ntohs(iph->frag_off) & IP_DF) + && (offset || (ntohs(iph->frag_off) & IP_MF))) { + limpk("DF set and offset=%u, MF=%u.\n", + offset, ntohs(iph->frag_off) & IP_MF); + return 0; + } + + /* CHECK: Zero-sized fragments. */ + if ((offset || (ntohs(iph->frag_off) & IP_MF)) + && datalen == 0) { + limpk("Zero size fragment offset=%u\n", offset); + return 0; + } + + /* Note: we can have even middle fragments smaller than this: + consider a large packet passing through a 600MTU then + 576MTU link: this gives a fragment of 24 data bytes. But + everyone packs fragments largest first, hence a fragment + can't START before 576 - MAX_IP_HEADER_LEN. */ + + /* Used to be min-size 576: I recall Alan Cox saying ax25 goes + down to 128 (576 taken from RFC 791: All hosts must be + prepared to accept datagrams of up to 576 octets). Use 128 + here. */ +#define MIN_LIKELY_MTU 128 + /* CHECK: Min size of first frag = 128. */ + if ((ntohs(iph->frag_off) & IP_MF) + && offset == 0 + && ntohs(iph->tot_len) < MIN_LIKELY_MTU) { + limpk("First fragment size %u < %u\n", ntohs(iph->tot_len), + MIN_LIKELY_MTU); + return 0; + } + + /* CHECK: Min offset of frag = 128 - IP hdr len. */ + if (offset && offset * 8 < MIN_LIKELY_MTU - iph->ihl * 4) { + limpk("Fragment starts at %u < %u\n", offset * 8, + MIN_LIKELY_MTU - iph->ihl * 4); + return 0; + } + + /* CHECK: Protocol specification non-zero. */ + if (iph->protocol == 0) { + limpk("Zero protocol\n"); + return 0; + } + + /* CHECK: Do not use what is unused. + * First bit of fragmentation flags should be unused. + * May be used by OS fingerprinting tools. + * 04 Jun 2002, Maciej Soltysiak, solt@dns.toxicfilms.tv + */ + if (ntohs(iph->frag_off)>>15) { + limpk("IP unused bit set\n"); + return 0; + } + + /* Per-protocol checks. */ + switch (iph->protocol) { + case IPPROTO_ICMP: + return check_icmp(protoh, datalen, offset, + (ntohs(iph->frag_off) & IP_MF), + embedded); + + case IPPROTO_UDP: + return check_udp(iph, protoh, datalen, offset, + (ntohs(iph->frag_off) & IP_MF), + embedded); + + case IPPROTO_TCP: + return check_tcp(iph, protoh, datalen, offset, + (ntohs(iph->frag_off) & IP_MF), + embedded); + default: + /* Ignorance is bliss. */ + return 1; + } +} + +static int +match(const struct sk_buff *skb, + const struct net_device *in, + const struct net_device *out, + const void *matchinfo, + int offset, + const void *hdr, + u_int16_t datalen, + int *hotdrop) +{ + return !check_ip(skb->nh.iph, skb->len, 0); +} + +/* Called when user tries to insert an entry of this type. */ +static int +checkentry(const char *tablename, + const struct ipt_ip *ip, + void *matchinfo, + unsigned int matchsize, + unsigned int hook_mask) +{ + if (matchsize != IPT_ALIGN(0)) + return 0; + + return 1; +} + +static struct ipt_match unclean_match += { { NULL, NULL }, "unclean", &match, &checkentry, NULL, THIS_MODULE }; + +static int __init init(void) +{ + return ipt_register_match(&unclean_match); +} + +static void __exit fini(void) +{ + ipt_unregister_match(&unclean_match); +} + +module_init(init); +module_exit(fini); +MODULE_LICENSE("GPL"); diff -Nur --exclude '*.orig' linux-2.6.6-rc1.org/net/ipv4/tcp_ipv4.c linux-2.6.6-rc1/net/ipv4/tcp_ipv4.c --- linux-2.6.6-rc1.org/net/ipv4/tcp_ipv4.c 2004-04-15 03:34:35.000000000 +0200 +++ linux-2.6.6-rc1/net/ipv4/tcp_ipv4.c 2004-04-19 10:46:08.000000000 +0200 @@ -2670,6 +2670,7 @@ EXPORT_SYMBOL(tcp_v4_connect); EXPORT_SYMBOL(tcp_v4_do_rcv); EXPORT_SYMBOL(tcp_v4_lookup_listener); +EXPORT_SYMBOL(tcp_v4_lookup); EXPORT_SYMBOL(tcp_v4_rebuild_header); EXPORT_SYMBOL(tcp_v4_remember_stamp); EXPORT_SYMBOL(tcp_v4_send_check); diff -Nur --exclude '*.orig' linux-2.6.6-rc1.org/net/ipv4/udp.c linux-2.6.6-rc1/net/ipv4/udp.c --- linux-2.6.6-rc1.org/net/ipv4/udp.c 2004-04-19 10:38:52.000000000 +0200 +++ linux-2.6.6-rc1/net/ipv4/udp.c 2004-04-19 10:46:08.000000000 +0200 @@ -1558,6 +1558,7 @@ EXPORT_SYMBOL(udp_port_rover); EXPORT_SYMBOL(udp_prot); EXPORT_SYMBOL(udp_sendmsg); +EXPORT_SYMBOL(udp_v4_lookup); #ifdef CONFIG_PROC_FS EXPORT_SYMBOL(udp_proc_register);