diff -Nur --exclude '*.orig' linux-2.6.6-rc1.org/include/linux/netfilter.h linux-2.6.6-rc1/include/linux/netfilter.h --- linux-2.6.6-rc1.org/include/linux/netfilter.h 2004-04-16 08:59:08.000000000 +0200 +++ linux-2.6.6-rc1/include/linux/netfilter.h 2004-04-16 09:17:13.000000000 +0200 @@ -137,12 +137,14 @@ /* This is gross, but inline doesn't cut it for avoiding the function call in fast path: gcc doesn't inline (needs value tracking?). --RR */ #ifdef CONFIG_NETFILTER_DEBUG -#define NF_HOOK(pf, hook, skb, indev, outdev, okfn) \ - nf_hook_slow((pf), (hook), (skb), (indev), (outdev), (okfn), INT_MIN) +#define NF_HOOK_COND(pf, hook, skb, indev, outdev, okfn, cond) \ +(!(cond) \ + ? (okfn)(skb) \ + : nf_hook_slow((pf), (hook), (skb), (indev), (outdev), (okfn), INT_MIN)) #define NF_HOOK_THRESH nf_hook_slow #else -#define NF_HOOK(pf, hook, skb, indev, outdev, okfn) \ -(list_empty(&nf_hooks[(pf)][(hook)]) \ +#define NF_HOOK_COND(pf, hook, skb, indev, outdev, okfn, cond) \ +(!(cond) || list_empty(&nf_hooks[(pf)][(hook)]) \ ? (okfn)(skb) \ : nf_hook_slow((pf), (hook), (skb), (indev), (outdev), (okfn), INT_MIN)) #define NF_HOOK_THRESH(pf, hook, skb, indev, outdev, okfn, thresh) \ @@ -150,6 +152,8 @@ ? (okfn)(skb) \ : nf_hook_slow((pf), (hook), (skb), (indev), (outdev), (okfn), (thresh))) #endif +#define NF_HOOK(pf, hook, skb, indev, outdev, okfn) \ + NF_HOOK_COND((pf), (hook), (skb), (indev), (outdev), (okfn), 1) int nf_hook_slow(int pf, unsigned int hook, struct sk_buff *skb, struct net_device *indev, struct net_device *outdev, @@ -182,7 +186,24 @@ #else /* !CONFIG_NETFILTER */ #define NF_HOOK(pf, hook, skb, indev, outdev, okfn) (okfn)(skb) +#define NF_HOOK_COND(pf, hook, skb, indev, outdev, okfn, cond) (okfn)(skb) #endif /*CONFIG_NETFILTER*/ +#ifdef CONFIG_XFRM +#ifdef CONFIG_IP_NF_NAT_NEEDED +struct flowi; +extern void nf_nat_decode_session4(struct sk_buff *skb, struct flowi *fl); + +static inline void +nf_nat_decode_session(struct sk_buff *skb, struct flowi *fl, int family) +{ + if (family == AF_INET) + nf_nat_decode_session4(skb, fl); +} +#else /* CONFIG_IP_NF_NAT_NEEDED */ +#define nf_nat_decode_session(skb,fl,family) +#endif /* CONFIG_IP_NF_NAT_NEEDED */ +#endif /* CONFIG_XFRM */ + #endif /*__KERNEL__*/ #endif /*__LINUX_NETFILTER_H*/ 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-16 08:59:08.000000000 +0200 +++ linux-2.6.6-rc1/include/linux/netfilter_ipv4/ip_conntrack.h 2004-04-16 09:18:58.000000000 +0200 @@ -64,6 +64,10 @@ }; /* Add protocol helper include file here */ +#include +#include +#include +#include #include #include #include @@ -71,6 +75,10 @@ /* per expectation: application helper private data */ union ip_conntrack_expect_help { /* insert conntrack helper private data (expect) here */ + struct ip_ct_talk_expect exp_talk_info; + struct ip_ct_rsh_expect exp_rsh_info; + struct ip_ct_mms_expect exp_mms_info; + struct ip_ct_h225_expect exp_h225_info; struct ip_ct_amanda_expect exp_amanda_info; struct ip_ct_ftp_expect exp_ftp_info; struct ip_ct_irc_expect exp_irc_info; @@ -85,6 +93,10 @@ /* per conntrack: application helper private data */ union ip_conntrack_help { /* insert conntrack helper private data (master) here */ + struct ip_ct_talk_master ct_talk_info; + struct ip_ct_rsh_master ct_rsh_info; + struct ip_ct_mms_master ct_mms_info; + struct ip_ct_h225_master ct_h225_info; struct ip_ct_ftp_master ct_ftp_info; struct ip_ct_irc_master ct_irc_info; }; @@ -207,6 +219,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_h323.h linux-2.6.6-rc1/include/linux/netfilter_ipv4/ip_conntrack_h323.h --- linux-2.6.6-rc1.org/include/linux/netfilter_ipv4/ip_conntrack_h323.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.6-rc1/include/linux/netfilter_ipv4/ip_conntrack_h323.h 2004-04-16 09:17:05.000000000 +0200 @@ -0,0 +1,31 @@ +#ifndef _IP_CONNTRACK_H323_H +#define _IP_CONNTRACK_H323_H +/* H.323 connection tracking. */ + +#ifdef __KERNEL__ +/* Protects H.323 related data */ +#include +DECLARE_LOCK_EXTERN(ip_h323_lock); +#endif + +/* Default H.225 port */ +#define H225_PORT 1720 + +/* This structure is per expected connection */ +struct ip_ct_h225_expect { + u_int16_t port; /* Port of the H.225 helper/RTCP/RTP channel */ + enum ip_conntrack_dir dir; /* Direction of the original connection */ + unsigned int offset; /* offset of the address in the payload */ +}; + +/* This structure exists only once per master */ +struct ip_ct_h225_master { + int is_h225; /* H.225 or H.245 connection */ +#ifdef CONFIG_IP_NF_NAT_NEEDED + enum ip_conntrack_dir dir; /* Direction of the original connection */ + u_int32_t seq[IP_CT_DIR_MAX]; /* Exceptional packet mangling for signal addressess... */ + unsigned int offset[IP_CT_DIR_MAX]; /* ...and the offset of the addresses in the payload */ +#endif +}; + +#endif /* _IP_CONNTRACK_H323_H */ diff -Nur --exclude '*.orig' linux-2.6.6-rc1.org/include/linux/netfilter_ipv4/ip_conntrack_mms.h linux-2.6.6-rc1/include/linux/netfilter_ipv4/ip_conntrack_mms.h --- linux-2.6.6-rc1.org/include/linux/netfilter_ipv4/ip_conntrack_mms.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.6-rc1/include/linux/netfilter_ipv4/ip_conntrack_mms.h 2004-04-16 09:17:32.000000000 +0200 @@ -0,0 +1,31 @@ +#ifndef _IP_CONNTRACK_MMS_H +#define _IP_CONNTRACK_MMS_H +/* MMS tracking. */ + +#ifdef __KERNEL__ +#include + +DECLARE_LOCK_EXTERN(ip_mms_lock); + +#define MMS_PORT 1755 +#define MMS_SRV_MSG_ID 196610 + +#define MMS_SRV_MSG_OFFSET 36 +#define MMS_SRV_UNICODE_STRING_OFFSET 60 +#define MMS_SRV_CHUNKLENLV_OFFSET 16 +#define MMS_SRV_CHUNKLENLM_OFFSET 32 +#define MMS_SRV_MESSAGELENGTH_OFFSET 8 +#endif + +/* This structure is per expected connection */ +struct ip_ct_mms_expect { + u_int32_t len; + u_int32_t padding; + u_int16_t port; +}; + +/* This structure exists only once per master */ +struct ip_ct_mms_master { +}; + +#endif /* _IP_CONNTRACK_MMS_H */ diff -Nur --exclude '*.orig' linux-2.6.6-rc1.org/include/linux/netfilter_ipv4/ip_conntrack_quake3.h linux-2.6.6-rc1/include/linux/netfilter_ipv4/ip_conntrack_quake3.h --- linux-2.6.6-rc1.org/include/linux/netfilter_ipv4/ip_conntrack_quake3.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.6-rc1/include/linux/netfilter_ipv4/ip_conntrack_quake3.h 2004-04-16 09:18:43.000000000 +0200 @@ -0,0 +1,21 @@ +#ifndef _IP_CT_QUAKE3 +#define _IP_CT_QUAKE3 + +/* Don't confuse with 27960, often used as the Server Port */ +#define QUAKE3_MASTER_PORT 27950 + +struct quake3_search { + const char marker[4]; /* always 0xff 0xff 0xff 0xff ? */ + const char *pattern; + size_t plen; +}; + +/* This structure is per expected connection */ +struct ip_ct_quake3_expect { +}; + +/* This structure exists only once per master */ +struct ip_ct_quake3_master { +}; + +#endif /* _IP_CT_QUAKE3 */ diff -Nur --exclude '*.orig' linux-2.6.6-rc1.org/include/linux/netfilter_ipv4/ip_conntrack_rpc.h linux-2.6.6-rc1/include/linux/netfilter_ipv4/ip_conntrack_rpc.h --- linux-2.6.6-rc1.org/include/linux/netfilter_ipv4/ip_conntrack_rpc.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.6-rc1/include/linux/netfilter_ipv4/ip_conntrack_rpc.h 2004-04-16 09:18:44.000000000 +0200 @@ -0,0 +1,68 @@ +/* RPC extension for IP connection tracking, Version 2.2 + * (C) 2000 by Marcelo Barbosa Lima + * - original rpc tracking module + * - "recent" connection handling for kernel 2.3+ netfilter + * + * (C) 2001 by Rusty Russell + * - upgraded conntrack modules to oldnat api - kernel 2.4.0+ + * + * (C) 2002 by Ian (Larry) Latter + * - upgraded conntrack modules to newnat api - kernel 2.4.20+ + * - extended matching to support filtering on procedures + * + * ip_conntrack_rpc.h,v 2.2 2003/01/12 18:30:00 + * + * 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. + ** + */ + +#include +#include +#include +#include +#include + +#include + +#ifndef _IP_CONNTRACK_RPC_H +#define _IP_CONNTRACK_RPC_H + +#define RPC_PORT 111 + + +/* Datum in RPC packets are encoded in XDR */ +#define IXDR_GET_INT32(buf) ((u_int32_t) ntohl((uint32_t)*buf)) + +/* Fast timeout, to deny DoS atacks */ +#define EXP (60 * HZ) + +/* Normal timeouts */ +#define EXPIRES (180 * HZ) + +/* For future conections RPC, using client's cache bindings + * I'll use ip_conntrack_lock to lock these lists */ + +/* This identifies each request and stores protocol */ +struct request_p { + struct list_head list; + + u_int32_t xid; + u_int32_t ip; + u_int16_t port; + + /* Protocol */ + u_int16_t proto; + + struct timer_list timeout; +}; + +static inline int request_p_cmp(const struct request_p *p, u_int32_t xid, + u_int32_t ip, u_int32_t port) { + return (p->xid == xid && p->ip == ip && p->port); + +} + +#endif /* _IP_CONNTRACK_RPC_H */ diff -Nur --exclude '*.orig' linux-2.6.6-rc1.org/include/linux/netfilter_ipv4/ip_conntrack_rsh.h linux-2.6.6-rc1/include/linux/netfilter_ipv4/ip_conntrack_rsh.h --- linux-2.6.6-rc1.org/include/linux/netfilter_ipv4/ip_conntrack_rsh.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.6-rc1/include/linux/netfilter_ipv4/ip_conntrack_rsh.h 2004-04-16 09:18:45.000000000 +0200 @@ -0,0 +1,35 @@ +/* RSH extension for IP connection tracking, Version 1.0 + * (C) 2002 by Ian (Larry) Latter + * based on HW's ip_conntrack_irc.c + * + * ip_conntrack_rsh.c,v 1.0 2002/07/17 14:49:26 + * + * 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. + */ +#ifndef _IP_CONNTRACK_RSH_H +#define _IP_CONNTRACK_RSH_H + +#ifdef __KERNEL__ +#include + +DECLARE_LOCK_EXTERN(ip_rsh_lock); +#endif + + +#define RSH_PORT 514 + +/* This structure is per expected connection */ +struct ip_ct_rsh_expect +{ + u_int16_t port; +}; + +/* This structure exists only once per master */ +struct ip_ct_rsh_master { +}; + +#endif /* _IP_CONNTRACK_RSH_H */ + diff -Nur --exclude '*.orig' linux-2.6.6-rc1.org/include/linux/netfilter_ipv4/ip_conntrack_talk.h linux-2.6.6-rc1/include/linux/netfilter_ipv4/ip_conntrack_talk.h --- linux-2.6.6-rc1.org/include/linux/netfilter_ipv4/ip_conntrack_talk.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.6-rc1/include/linux/netfilter_ipv4/ip_conntrack_talk.h 2004-04-16 09:18:58.000000000 +0200 @@ -0,0 +1,152 @@ +#ifndef _IP_CONNTRACK_TALK_H +#define _IP_CONNTRACK_TALK_H +/* TALK tracking. */ + +#ifdef __KERNEL__ +#include +#include + +/* Protects talk part of conntracks */ +DECLARE_LOCK_EXTERN(ip_talk_lock); +#endif + + +#define TALK_PORT 517 +#define NTALK_PORT 518 + +/* talk structures and constants from */ + +/* + * 4.3BSD struct sockaddr + */ +struct talk_addr { + u_int16_t ta_family; + u_int16_t ta_port; + u_int32_t ta_addr; + u_int32_t ta_junk1; + u_int32_t ta_junk2; +}; + +#define TALK_OLD_NSIZE 9 +#define TALK_NSIZE 12 +#define TALK_TTY_NSIZE 16 + +/* + * Client->server request message formats. + */ +struct talk_msg { + u_char type; /* request type, see below */ + char l_name[TALK_OLD_NSIZE];/* caller's name */ + char r_name[TALK_OLD_NSIZE];/* callee's name */ + u_char pad; + u_int32_t id_num; /* message id */ + int32_t pid; /* caller's process id */ + char r_tty[TALK_TTY_NSIZE];/* callee's tty name */ + struct talk_addr addr; /* old (4.3) style */ + struct talk_addr ctl_addr; /* old (4.3) style */ +}; + +struct ntalk_msg { + u_char vers; /* protocol version */ + u_char type; /* request type, see below */ + u_char answer; /* not used */ + u_char pad; + u_int32_t id_num; /* message id */ + struct talk_addr addr; /* old (4.3) style */ + struct talk_addr ctl_addr; /* old (4.3) style */ + int32_t pid; /* caller's process id */ + char l_name[TALK_NSIZE];/* caller's name */ + char r_name[TALK_NSIZE];/* callee's name */ + char r_tty[TALK_TTY_NSIZE];/* callee's tty name */ +}; + +struct ntalk2_msg { + u_char vers; /* talk protocol version */ + u_char type; /* request type */ + u_char answer; /* */ + u_char extended; /* !0 if additional parts */ + u_int32_t id_num; /* message id number (dels) */ + struct talk_addr addr; /* target address */ + struct talk_addr ctl_addr; /* reply to address */ + int32_t pid; /* caller's process id */ + char l_name[TALK_NSIZE]; /* caller's name */ + char r_name[TALK_NSIZE]; /* callee's name */ + char r_tty[TALK_TTY_NSIZE]; /* callee's tty */ +}; + +/* + * Server->client response message formats. + */ +struct talk_response { + u_char type; /* type of request message, see below */ + u_char answer; /* response to request message, see below */ + u_char pad[2]; + u_int32_t id_num; /* message id */ + struct talk_addr addr; /* address for establishing conversation */ +}; + +struct ntalk_response { + u_char vers; /* protocol version */ + u_char type; /* type of request message, see below */ + u_char answer; /* response to request message, see below */ + u_char pad; + u_int32_t id_num; /* message id */ + struct talk_addr addr; /* address for establishing conversation */ +}; + +struct ntalk2_response { + u_char vers; /* protocol version */ + u_char type; /* type of request message */ + u_char answer; /* response to request */ + u_char rvers; /* Version of answering vers*/ + u_int32_t id_num; /* message id number */ + struct talk_addr addr; /* address for connection */ + /* This is at the end to compatiblize this with NTALK version. */ + char r_name[TALK_NSIZE]; /* callee's name */ +}; + +#define TALK_STR(data, talk_str, member) ((struct talk_str *)data)->member) +#define TALK_RESP(data, ver, member) (ver ? ((struct ntalk_response *)data)->member : ((struct talk_response *)data)->member) +#define TALK_MSG(data, ver, member) (ver ? ((struct ntalk_msg *)data)->member : ((struct talk_msg *)data)->member) + +#define TALK_VERSION 0 /* protocol versions */ +#define NTALK_VERSION 1 +#define NTALK2_VERSION 2 + +/* message type values */ +#define LEAVE_INVITE 0 /* leave invitation with server */ +#define LOOK_UP 1 /* check for invitation by callee */ +#define DELETE 2 /* delete invitation by caller */ +#define ANNOUNCE 3 /* announce invitation by caller */ +/* NTALK2 */ +#define REPLY_QUERY 4 /* request reply data from local daemon */ + +/* answer values */ +#define SUCCESS 0 /* operation completed properly */ +#define NOT_HERE 1 /* callee not logged in */ +#define FAILED 2 /* operation failed for unexplained reason */ +#define MACHINE_UNKNOWN 3 /* caller's machine name unknown */ +#define PERMISSION_DENIED 4 /* callee's tty doesn't permit announce */ +#define UNKNOWN_REQUEST 5 /* request has invalid type value */ +#define BADVERSION 6 /* request has invalid protocol version */ +#define BADADDR 7 /* request has invalid addr value */ +#define BADCTLADDR 8 /* request has invalid ctl_addr value */ +/* NTALK2 */ +#define NO_CALLER 9 /* no-one calling answer from REPLY */ +#define TRY_HERE 10 /* Not on this machine, try this */ +#define SELECTIVE_REFUSAL 11 /* User Filter refusal. */ +#define MAX_RESPONSE_TYPE 11 /* Make sure this is updated */ + +/* We don't really need much for talk */ +struct ip_ct_talk_expect +{ + /* Port that was to be used */ + u_int16_t port; +}; + +/* This structure exists only once per master */ +struct ip_ct_talk_master +{ +}; + +#endif /* _IP_CONNTRACK_TALK_H */ 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-16 09:18:47.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-16 09:15:41.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-16 09:15:43.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_ROUTE.h linux-2.6.6-rc1/include/linux/netfilter_ipv4/ipt_ROUTE.h --- linux-2.6.6-rc1.org/include/linux/netfilter_ipv4/ipt_ROUTE.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.6-rc1/include/linux/netfilter_ipv4/ipt_ROUTE.h 2004-04-16 09:15:44.000000000 +0200 @@ -0,0 +1,22 @@ +/* Header file for iptables ipt_ROUTE target + * + * (C) 2002 by Cédric de Launois + * + * This software is distributed under GNU GPL v2, 1991 + */ +#ifndef _IPT_ROUTE_H_target +#define _IPT_ROUTE_H_target + +#define IPT_ROUTE_IFNAMSIZ 16 + +struct ipt_route_target_info { + char oif[IPT_ROUTE_IFNAMSIZ]; /* Output Interface Name */ + char iif[IPT_ROUTE_IFNAMSIZ]; /* Input Interface Name */ + u_int32_t gw; /* IP address of gateway */ + u_int8_t flags; +}; + +/* Values for "flags" field */ +#define IPT_ROUTE_CONTINUE 0x01 + +#endif /*_IPT_ROUTE_H_target*/ diff -Nur --exclude '*.orig' linux-2.6.6-rc1.org/include/linux/netfilter_ipv4/ipt_TCPLAG.h linux-2.6.6-rc1/include/linux/netfilter_ipv4/ipt_TCPLAG.h --- linux-2.6.6-rc1.org/include/linux/netfilter_ipv4/ipt_TCPLAG.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.6-rc1/include/linux/netfilter_ipv4/ipt_TCPLAG.h 2004-04-16 09:15:55.000000000 +0200 @@ -0,0 +1,10 @@ +#ifndef _IPT_TCPLAG_H +#define _IPT_TCPLAG_H + +struct ipt_tcplag +{ + unsigned char level; + unsigned char prefix[ 15 ]; +}; + +#endif 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-16 09:16:25.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-16 09:16:27.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-16 09:15:41.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-16 09:18:18.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_rpc.h linux-2.6.6-rc1/include/linux/netfilter_ipv4/ipt_rpc.h --- linux-2.6.6-rc1.org/include/linux/netfilter_ipv4/ipt_rpc.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.6-rc1/include/linux/netfilter_ipv4/ipt_rpc.h 2004-04-16 09:18:44.000000000 +0200 @@ -0,0 +1,35 @@ +/* RPC extension for IP netfilter matching, Version 2.2 + * (C) 2000 by Marcelo Barbosa Lima + * - original rpc tracking module + * - "recent" connection handling for kernel 2.3+ netfilter + * + * (C) 2001 by Rusty Russell + * - upgraded conntrack modules to oldnat api - kernel 2.4.0+ + * + * (C) 2002 by Ian (Larry) Latter + * - upgraded conntrack modules to newnat api - kernel 2.4.20+ + * - extended matching to support filtering on procedures + * + * ipt_rpc.h.c,v 2.2 2003/01/12 18:30:00 + * + * 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. + ** + */ + +#ifndef _IPT_RPC_H +#define _IPT_RPC_H + +struct ipt_rpc_data; + +struct ipt_rpc_info { + int inverse; + int strict; + const char c_procs[1408]; + int i_procs; + struct ipt_rpc_data *data; +}; + +#endif /* _IPT_RPC_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-16 09:19:37.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/linux/netfilter_ipv4.h linux-2.6.6-rc1/include/linux/netfilter_ipv4.h --- linux-2.6.6-rc1.org/include/linux/netfilter_ipv4.h 2004-04-16 08:59:08.000000000 +0200 +++ linux-2.6.6-rc1/include/linux/netfilter_ipv4.h 2004-04-16 09:17:09.000000000 +0200 @@ -7,6 +7,8 @@ #include #include +#include +#include /* IP Cache bits. */ /* Src IP address. */ @@ -85,6 +87,58 @@ Returns true or false. */ extern int skb_ip_make_writable(struct sk_buff **pskb, unsigned int writable_len); + +#ifdef CONFIG_XFRM +#include +#include + +static inline int nf_hook_input_cond(struct sk_buff *skb) +{ + return !skb->sp || skb->sp->decap_done; +} + +static inline int +nf_xfrm_local_done(struct sk_buff *skb, struct inet_protocol *ipprot) +{ + return skb->sp && !skb->sp->decap_done + && (!ipprot || !ipprot->xfrm_prot); +} + +static inline int nf_xfrm_nonlocal_done(struct sk_buff *skb) +{ + return skb->sp && !skb->sp->decap_done + && !(((struct rtable *)skb->dst)->rt_flags&RTCF_LOCAL); +} + +extern int nf_rcv_postxfrm_local(struct sk_buff *skb); +extern int nf_rcv_postxfrm_nonlocal(struct sk_buff *skb); +#else /* CONFIG_XFRM */ +static inline int nf_hook_input_cond(struct sk_buff *skb) +{ + return 1; +} + +static inline int +nf_xfrm_local_done(struct sk_buff *skb, struct inet_protocol *ipprot) +{ + return 0; +} + +static inline int nf_xfrm_nonlocal_done(struct sk_buff *skb) +{ + return 0; +} + +static inline int nf_rcv_postxfrm_local(struct sk_buff *skb) +{ + return 0; +} + +static inline int nf_rcv_postxfrm_nonlocal(struct sk_buff *skb) +{ + return 0; +} +#endif /* CONFIG_XFRM */ #endif /*__KERNEL__*/ #endif /*__LINUX_IP_NETFILTER_H*/ diff -Nur --exclude '*.orig' linux-2.6.6-rc1.org/include/linux/netfilter_ipv6/ip6t_ROUTE.h linux-2.6.6-rc1/include/linux/netfilter_ipv6/ip6t_ROUTE.h --- linux-2.6.6-rc1.org/include/linux/netfilter_ipv6/ip6t_ROUTE.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.6-rc1/include/linux/netfilter_ipv6/ip6t_ROUTE.h 2004-04-16 09:15:44.000000000 +0200 @@ -0,0 +1,22 @@ +/* Header file for iptables ip6t_ROUTE target + * + * (C) 2003 by Cédric de Launois + * + * This software is distributed under GNU GPL v2, 1991 + */ +#ifndef _IPT_ROUTE_H_target +#define _IPT_ROUTE_H_target + +#define IP6T_ROUTE_IFNAMSIZ 16 + +struct ip6t_route_target_info { + char oif[IP6T_ROUTE_IFNAMSIZ]; /* Output Interface Name */ + char iif[IP6T_ROUTE_IFNAMSIZ]; /* Input Interface Name */ + u_int32_t gw[4]; /* IPv6 address of gateway */ + u_int8_t flags; +}; + +/* Values for "flags" field */ +#define IP6T_ROUTE_CONTINUE 0x01 + +#endif /*_IP6T_ROUTE_H_target*/ diff -Nur --exclude '*.orig' linux-2.6.6-rc1.org/include/linux/netfilter_ipv6/ip6t_owner.h linux-2.6.6-rc1/include/linux/netfilter_ipv6/ip6t_owner.h --- linux-2.6.6-rc1.org/include/linux/netfilter_ipv6/ip6t_owner.h 2004-04-15 03:35:08.000000000 +0200 +++ linux-2.6.6-rc1/include/linux/netfilter_ipv6/ip6t_owner.h 2004-04-16 09:18:17.000000000 +0200 @@ -6,12 +6,14 @@ #define IP6T_OWNER_GID 0x02 #define IP6T_OWNER_PID 0x04 #define IP6T_OWNER_SID 0x08 +#define IP6T_OWNER_COMM 0x10 struct ip6t_owner_info { uid_t uid; gid_t gid; pid_t pid; pid_t sid; + char comm[16]; u_int8_t match, invert; /* flags */ }; diff -Nur --exclude '*.orig' linux-2.6.6-rc1.org/include/net/ip.h linux-2.6.6-rc1/include/net/ip.h --- linux-2.6.6-rc1.org/include/net/ip.h 2004-04-15 03:35:18.000000000 +0200 +++ linux-2.6.6-rc1/include/net/ip.h 2004-04-16 09:17:07.000000000 +0200 @@ -30,6 +30,8 @@ #include #include #include +#include +#include #include #include @@ -48,6 +50,7 @@ #define IPSKB_TRANSLATED 2 #define IPSKB_FORWARDED 4 #define IPSKB_XFRM_TUNNEL_SIZE 8 +#define IPSKB_XFRM_TRANSFORMED 16 }; struct ipcm_cookie @@ -212,6 +215,12 @@ __ip_select_ident(iph, dst, more); } +extern inline int ip_dst_output(struct sk_buff *skb) +{ + return NF_HOOK_COND(PF_INET, NF_IP_POST_ROUTING, skb, NULL, + skb->dst->dev, dst_output, skb->dst->xfrm != NULL); +} + /* * Map a multicast IP onto multicast MAC for type ethernet. */ diff -Nur --exclude '*.orig' linux-2.6.6-rc1.org/include/net/protocol.h linux-2.6.6-rc1/include/net/protocol.h --- linux-2.6.6-rc1.org/include/net/protocol.h 2004-04-15 03:34:36.000000000 +0200 +++ linux-2.6.6-rc1/include/net/protocol.h 2004-04-16 09:17:09.000000000 +0200 @@ -39,6 +39,7 @@ int (*handler)(struct sk_buff *skb); void (*err_handler)(struct sk_buff *skb, u32 info); int no_policy; + int xfrm_prot; }; #if defined(CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE) 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-16 09:18:14.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-16 09:18:14.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/include/net/xfrm.h linux-2.6.6-rc1/include/net/xfrm.h --- linux-2.6.6-rc1.org/include/net/xfrm.h 2004-04-15 03:35:36.000000000 +0200 +++ linux-2.6.6-rc1/include/net/xfrm.h 2004-04-16 09:17:09.000000000 +0200 @@ -540,6 +540,9 @@ { atomic_t refcnt; int len; +#ifdef CONFIG_NETFILTER + int decap_done; +#endif struct sec_decap_state x[XFRM_MAX_DEPTH]; }; diff -Nur --exclude '*.orig' linux-2.6.6-rc1.org/net/core/netfilter.c linux-2.6.6-rc1/net/core/netfilter.c --- linux-2.6.6-rc1.org/net/core/netfilter.c 2004-04-16 08:59:08.000000000 +0200 +++ linux-2.6.6-rc1/net/core/netfilter.c 2004-04-16 09:17:13.000000000 +0200 @@ -27,6 +27,8 @@ #include #include #include +#include +#include #include /* In this code, we can be waiting indefinitely for userspace to @@ -638,7 +640,6 @@ #ifdef CONFIG_IP_ROUTE_FWMARK fl.nl_u.ip4_u.fwmark = (*pskb)->nfmark; #endif - fl.proto = iph->protocol; if (ip_route_output_key(&rt, &fl) != 0) return -1; @@ -665,6 +666,20 @@ if ((*pskb)->dst->error) return -1; +#ifdef CONFIG_XFRM + if (!(IPCB(*pskb)->flags & IPSKB_XFRM_TRANSFORMED)) { + struct xfrm_policy_afinfo *afinfo; + + afinfo = xfrm_policy_get_afinfo(AF_INET); + if (afinfo != NULL) { + afinfo->decode_session(*pskb, &fl); + xfrm_policy_put_afinfo(afinfo); + if (xfrm_lookup(&(*pskb)->dst, &fl, (*pskb)->sk, 0) != 0) + return -1; + } + } +#endif + /* Change in oif may mean change in hh_len. */ hh_len = (*pskb)->dst->dev->hard_header_len; if (skb_headroom(*pskb) < hh_len) { @@ -682,6 +697,71 @@ return 0; } +#ifdef CONFIG_XFRM +inline int nf_rcv_postxfrm_nonlocal(struct sk_buff *skb) +{ + skb->sp->decap_done = 1; + dst_release(skb->dst); + skb->dst = NULL; + nf_reset(skb); + return netif_rx(skb); +} + +int nf_rcv_postxfrm_local(struct sk_buff *skb) +{ + __skb_push(skb, skb->data - skb->nh.raw); + /* Fix header len and checksum if last xfrm was transport mode */ + if (!skb->sp->x[skb->sp->len - 1].xvec->props.mode) { + skb->nh.iph->tot_len = htons(skb->len); + ip_send_check(skb->nh.iph); + } + return nf_rcv_postxfrm_nonlocal(skb); +} + +#ifdef CONFIG_IP_NF_NAT_NEEDED +#include +#include + +void nf_nat_decode_session4(struct sk_buff *skb, struct flowi *fl) +{ + struct ip_conntrack *ct; + struct ip_conntrack_tuple *t; + struct ip_nat_info_manip *m; + unsigned int i; + + if (skb->nfct == NULL) + return; + ct = (struct ip_conntrack *)skb->nfct->master; + + for (i = 0; i < ct->nat.info.num_manips; i++) { + m = &ct->nat.info.manips[i]; + t = &ct->tuplehash[m->direction].tuple; + + switch (m->hooknum) { + case NF_IP_PRE_ROUTING: + if (m->maniptype != IP_NAT_MANIP_DST) + break; + fl->fl4_dst = t->dst.ip; + if (t->dst.protonum == IPPROTO_TCP || + t->dst.protonum == IPPROTO_UDP) + fl->fl_ip_dport = t->dst.u.tcp.port; + break; +#ifdef CONFIG_IP_NF_NAT_LOCAL + case NF_IP_LOCAL_IN: + if (m->maniptype != IP_NAT_MANIP_SRC) + break; + fl->fl4_src = t->src.ip; + if (t->dst.protonum == IPPROTO_TCP || + t->dst.protonum == IPPROTO_UDP) + fl->fl_ip_sport = t->src.u.tcp.port; + break; +#endif + } + } +} +#endif /* CONFIG_IP_NF_NAT_NEEDED */ +#endif + int skb_ip_make_writable(struct sk_buff **pskb, unsigned int writable_len) { struct sk_buff *nskb; diff -Nur --exclude '*.orig' linux-2.6.6-rc1.org/net/ipv4/ah4.c linux-2.6.6-rc1/net/ipv4/ah4.c --- linux-2.6.6-rc1.org/net/ipv4/ah4.c 2004-04-15 03:35:37.000000000 +0200 +++ linux-2.6.6-rc1/net/ipv4/ah4.c 2004-04-16 09:17:09.000000000 +0200 @@ -145,6 +145,7 @@ err = -EHOSTUNREACH; goto error_nolock; } + IPCB(skb)->flags |= IPSKB_XFRM_TRANSFORMED; return NET_XMIT_BYPASS; error: @@ -343,6 +344,7 @@ .handler = xfrm4_rcv, .err_handler = ah4_err, .no_policy = 1, + .xfrm_prot = 1, }; static int __init ah4_init(void) diff -Nur --exclude '*.orig' linux-2.6.6-rc1.org/net/ipv4/esp4.c linux-2.6.6-rc1/net/ipv4/esp4.c --- linux-2.6.6-rc1.org/net/ipv4/esp4.c 2004-04-15 03:35:04.000000000 +0200 +++ linux-2.6.6-rc1/net/ipv4/esp4.c 2004-04-16 09:17:09.000000000 +0200 @@ -199,6 +199,7 @@ err = -EHOSTUNREACH; goto error_nolock; } + IPCB(skb)->flags |= IPSKB_XFRM_TRANSFORMED; return NET_XMIT_BYPASS; error: @@ -576,6 +577,7 @@ .handler = xfrm4_rcv, .err_handler = esp4_err, .no_policy = 1, + .xfrm_prot = 1, }; static int __init esp4_init(void) diff -Nur --exclude '*.orig' linux-2.6.6-rc1.org/net/ipv4/igmp.c linux-2.6.6-rc1/net/ipv4/igmp.c --- linux-2.6.6-rc1.org/net/ipv4/igmp.c 2004-04-15 03:36:01.000000000 +0200 +++ linux-2.6.6-rc1/net/ipv4/igmp.c 2004-04-16 09:17:07.000000000 +0200 @@ -342,7 +342,7 @@ pig->csum = ip_compute_csum((void *)skb->h.igmph, igmplen); return NF_HOOK(PF_INET, NF_IP_LOCAL_OUT, skb, NULL, skb->dev, - dst_output); + ip_dst_output); } static int grec_size(struct ip_mc_list *pmc, int type, int gdel, int sdel) @@ -672,7 +672,7 @@ ih->csum=ip_compute_csum((void *)ih, sizeof(struct igmphdr)); return NF_HOOK(PF_INET, NF_IP_LOCAL_OUT, skb, NULL, rt->u.dst.dev, - dst_output); + ip_dst_output); } static void igmp_gq_timer_expire(unsigned long data) diff -Nur --exclude '*.orig' linux-2.6.6-rc1.org/net/ipv4/ip_forward.c linux-2.6.6-rc1/net/ipv4/ip_forward.c --- linux-2.6.6-rc1.org/net/ipv4/ip_forward.c 2004-04-15 03:33:50.000000000 +0200 +++ linux-2.6.6-rc1/net/ipv4/ip_forward.c 2004-04-16 09:17:07.000000000 +0200 @@ -51,7 +51,7 @@ if (unlikely(opt->optlen)) ip_forward_options(skb); - return dst_output(skb); + return ip_dst_output(skb); } int ip_forward(struct sk_buff *skb) diff -Nur --exclude '*.orig' linux-2.6.6-rc1.org/net/ipv4/ip_input.c linux-2.6.6-rc1/net/ipv4/ip_input.c --- linux-2.6.6-rc1.org/net/ipv4/ip_input.c 2004-04-16 08:59:08.000000000 +0200 +++ linux-2.6.6-rc1/net/ipv4/ip_input.c 2004-04-16 09:17:13.000000000 +0200 @@ -206,10 +206,6 @@ __skb_pull(skb, ihl); - /* Free reference early: we don't need it any more, and it may - hold ip_conntrack module loaded indefinitely. */ - nf_reset(skb); - /* Point into the IP datagram, just past the header. */ skb->h.raw = skb->data; @@ -224,6 +220,13 @@ resubmit: hash = protocol & (MAX_INET_PROTOS - 1); raw_sk = sk_head(&raw_v4_htable[hash]); + ipprot = inet_protos[hash]; + smp_read_barrier_depends(); + + if (nf_xfrm_local_done(skb, ipprot)) { + nf_rcv_postxfrm_local(skb); + goto out; + } /* If there maybe a raw socket we must check - if not we * don't care less @@ -231,14 +234,15 @@ if (raw_sk) raw_v4_input(skb, skb->nh.iph, hash); - if ((ipprot = inet_protos[hash]) != NULL) { + if (ipprot != NULL) { int ret; - smp_read_barrier_depends(); - if (!ipprot->no_policy && - !xfrm4_policy_check(NULL, XFRM_POLICY_IN, skb)) { - kfree_skb(skb); - goto out; + if (!ipprot->no_policy) { + if (!xfrm4_policy_check(NULL, XFRM_POLICY_IN, skb)) { + kfree_skb(skb); + goto out; + } + nf_reset(skb); } ret = ipprot->handler(skb); if (ret < 0) { @@ -279,8 +283,8 @@ return 0; } - return NF_HOOK(PF_INET, NF_IP_LOCAL_IN, skb, skb->dev, NULL, - ip_local_deliver_finish); + return NF_HOOK_COND(PF_INET, NF_IP_LOCAL_IN, skb, skb->dev, NULL, + ip_local_deliver_finish, nf_hook_input_cond(skb)); } static inline int ip_rcv_finish(struct sk_buff *skb) @@ -297,6 +301,9 @@ goto drop; } + if (nf_xfrm_nonlocal_done(skb)) + return nf_rcv_postxfrm_nonlocal(skb); + #ifdef CONFIG_NET_CLS_ROUTE if (skb->dst->tclassid) { struct ip_rt_acct *st = ip_rt_acct + 256*smp_processor_id(); @@ -418,8 +425,8 @@ } } - return NF_HOOK(PF_INET, NF_IP_PRE_ROUTING, skb, dev, NULL, - ip_rcv_finish); + return NF_HOOK_COND(PF_INET, NF_IP_PRE_ROUTING, skb, dev, NULL, + ip_rcv_finish, nf_hook_input_cond(skb)); inhdr_error: IP_INC_STATS_BH(IpInHdrErrors); diff -Nur --exclude '*.orig' linux-2.6.6-rc1.org/net/ipv4/ip_output.c linux-2.6.6-rc1/net/ipv4/ip_output.c --- linux-2.6.6-rc1.org/net/ipv4/ip_output.c 2004-04-15 03:36:01.000000000 +0200 +++ linux-2.6.6-rc1/net/ipv4/ip_output.c 2004-04-16 09:17:07.000000000 +0200 @@ -123,6 +123,15 @@ return ttl; } +#ifdef CONFIG_NETFILTER +/* out-of-line copy is only required with netfilter */ +int ip_dst_output(struct sk_buff *skb) +{ + return NF_HOOK_COND(PF_INET, NF_IP_POST_ROUTING, skb, NULL, + skb->dst->dev, dst_output, skb->dst->xfrm != NULL); +} +#endif + /* * Add an ip header to a skbuff and send it out. * @@ -165,7 +174,7 @@ /* Send it out. */ return NF_HOOK(PF_INET, NF_IP_LOCAL_OUT, skb, NULL, rt->u.dst.dev, - dst_output); + ip_dst_output); } static inline int ip_finish_output2(struct sk_buff *skb) @@ -283,7 +292,7 @@ return ip_finish_output(skb); } -int ip_output(struct sk_buff *skb) +static inline int ip_output2(struct sk_buff *skb) { IP_INC_STATS(IpOutRequests); @@ -294,6 +303,16 @@ return ip_finish_output(skb); } +int ip_output(struct sk_buff *skb) +{ + int transformed = IPCB(skb)->flags & IPSKB_XFRM_TRANSFORMED; + + if (transformed) + nf_reset(skb); + return NF_HOOK_COND(PF_INET, NF_IP_LOCAL_OUT, skb, NULL, + skb->dst->dev, ip_output2, transformed); +} + int ip_queue_xmit(struct sk_buff *skb, int ipfragok) { struct sock *sk = skb->sk; @@ -387,7 +406,7 @@ skb->priority = sk->sk_priority; return NF_HOOK(PF_INET, NF_IP_LOCAL_OUT, skb, NULL, rt->u.dst.dev, - dst_output); + ip_dst_output); no_route: IP_INC_STATS(IpOutNoRoutes); @@ -1177,7 +1196,7 @@ /* Netfilter gets whole the not fragmented skb. */ err = NF_HOOK(PF_INET, NF_IP_LOCAL_OUT, skb, NULL, - skb->dst->dev, dst_output); + skb->dst->dev, ip_dst_output); if (err) { if (err > 0) err = inet->recverr ? net_xmit_errno(err) : 0; diff -Nur --exclude '*.orig' linux-2.6.6-rc1.org/net/ipv4/ipcomp.c linux-2.6.6-rc1/net/ipv4/ipcomp.c --- linux-2.6.6-rc1.org/net/ipv4/ipcomp.c 2004-04-15 03:35:37.000000000 +0200 +++ linux-2.6.6-rc1/net/ipv4/ipcomp.c 2004-04-16 09:17:09.000000000 +0200 @@ -231,6 +231,7 @@ err = -EHOSTUNREACH; goto error_nolock; } + IPCB(skb)->flags |= IPSKB_XFRM_TRANSFORMED; err = NET_XMIT_BYPASS; out_exit: @@ -407,6 +408,7 @@ .handler = xfrm4_rcv, .err_handler = ipcomp4_err, .no_policy = 1, + .xfrm_prot = 1, }; static int __init ipcomp4_init(void) diff -Nur --exclude '*.orig' linux-2.6.6-rc1.org/net/ipv4/ipip.c linux-2.6.6-rc1/net/ipv4/ipip.c --- linux-2.6.6-rc1.org/net/ipv4/ipip.c 2004-04-16 08:59:08.000000000 +0200 +++ linux-2.6.6-rc1/net/ipv4/ipip.c 2004-04-16 09:17:09.000000000 +0200 @@ -478,6 +478,11 @@ read_lock(&ipip_lock); if ((tunnel = ipip_tunnel_lookup(iph->saddr, iph->daddr)) != NULL) { + /* IPIP packets decapsulated by IPsec missed netfilter hooks */ + if (nf_xfrm_local_done(skb, NULL)) { + nf_rcv_postxfrm_local(skb); + return 0; + } if (!xfrm4_policy_check(NULL, XFRM_POLICY_IN, skb)) { kfree_skb(skb); return 0; diff -Nur --exclude '*.orig' linux-2.6.6-rc1.org/net/ipv4/ipmr.c linux-2.6.6-rc1/net/ipv4/ipmr.c --- linux-2.6.6-rc1.org/net/ipv4/ipmr.c 2004-04-15 03:34:48.000000000 +0200 +++ linux-2.6.6-rc1/net/ipv4/ipmr.c 2004-04-16 09:17:07.000000000 +0200 @@ -1120,7 +1120,7 @@ if (unlikely(opt->optlen)) ip_forward_options(skb); - return dst_output(skb); + return ip_dst_output(skb); } /* 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-16 08:59:09.000000000 +0200 +++ linux-2.6.6-rc1/net/ipv4/netfilter/Kconfig 2004-04-16 09:19:38.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 @@ -510,6 +515,38 @@ To compile it as a module, choose M here. If unsure, say N. +config IP_NF_TARGET_TARPIT + tristate "TARPIT target support" + depends on IP_NF_IPTABLES + ---help--- + Adds a TARPIT target to iptables, which captures and holds + incoming TCP connections using no local per-connection resources. + Connections are accepted, but immediately switched to the persist + state (0 byte window), in which the remote side stops sending data + and asks to continue every 60-240 seconds. Attempts to close the + connection are ignored, forcing the remote side to time out the + connection in 12-24 minutes. + + This offers similar functionality to LaBrea + but doesn't require dedicated + hardware or IPs. Any TCP port that you would normally DROP or REJECT + can instead become a tarpit. + + To tarpit connections to TCP port 80 destined for the current machine: + + iptables -A INPUT -p tcp -m tcp --dport 80 -j TARPIT + + To significantly slow down Code Red/Nimda-style scans of unused + address space, forward unused ip addresses to a Linux box not acting + as a router (e.g. "ip route 10.0.0.0 255.0.0.0 ip.of.linux.box" on a Cisco), + enable IP forwarding on the Linux box, and add: + + iptables -A FORWARD -p tcp -j TARPIT + iptables -A FORWARD -j DROP + + You probably don't want the conntrack module loaded while you are using + TARPIT, or you will be using resources per connection. + config IP_NF_TARGET_TCPMSS tristate "TCPMSS target support" depends on IP_NF_IPTABLES @@ -706,5 +743,114 @@ 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_ROUTE + tristate 'ROUTE target support' + depends on IP_NF_MANGLE + help + +config IP_NF_TARGET_TCPLAG + tristate 'TCPLAG target support' + depends on IP_NF_IPTABLES + 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_NAT_H323 + tristate + depends on IP_NF_CONNTRACK!=n && IP_NF_NAT!=n + default IP_NF_NAT if IP_NF_H323=y + default m if IP_NF_H323=m + +config IP_NF_H323 + tristate 'H.323 (netmeeting) support' + depends on IP_NF_CONNTRACK + help + +config IP_NF_NAT_MMS + tristate + depends on IP_NF_CONNTRACK!=n && IP_NF_NAT!=n + default IP_NF_NAT if IP_NF_MMS=y + default m if IP_NF_MMS=m + +config IP_NF_MMS + tristate 'MMS protocol support' + depends on IP_NF_CONNTRACK + 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_NAT_QUAKE3 + tristate + depends on IP_NF_CONNTRACK!=n && IP_NF_NAT !=n + default IP_NF_NAT if IP_NF_QUAKE3=y + default m if IP_NF_QUAKE3=m + +config IP_NF_QUAKE3 + tristate "Quake3 protocol support" + depends on IP_NF_CONNTRACK + help + +config IP_NF_MATCH_RPC + tristate 'RPC match support' + depends on IP_NF_CONNTRACK && IP_NF_IPTABLES + help + +config IP_NF_RSH + tristate 'RSH protocol support' + depends on IP_NF_CONNTRACK + help + +config IP_NF_CT_PROTO_SCTP + tristate 'SCTP protocol connection tracking support' + depends on IP_NF_CONNTRACK + help + +config IP_NF_NAT_TALK + tristate + depends on IP_NF_CONNTRACK!=n && IP_NF_NAT!=n + default IP_NF_NAT if IP_NF_TALK=y + default m if IP_NF_TALK=m +config IP_NF_TALK + tristate 'talk protocol 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-16 08:59:09.000000000 +0200 +++ linux-2.6.6-rc1/net/ipv4/netfilter/Makefile 2004-04-16 09:19:38.000000000 +0200 @@ -19,10 +19,39 @@ # connection tracking obj-$(CONFIG_IP_NF_CONNTRACK) += ip_conntrack.o +# talk protocol support +obj-$(CONFIG_IP_NF_TALK) += ip_conntrack_talk.o +ifdef CONFIG_IP_NF_TALK + export-objs += ip_conntrack_talk.o +endif +obj-$(CONFIG_IP_NF_NAT_TALK) += ip_nat_talk.o + + +# SCTP protocol connection tracking +obj-$(CONFIG_IP_NF_CT_PROTO_SCTP) += ip_conntrack_proto_sctp.o + +# H.323 support +obj-$(CONFIG_IP_NF_H323) += ip_conntrack_h323.o +ifdef CONFIG_IP_NF_H323 + export-objs += ip_conntrack_h323.o +endif +obj-$(CONFIG_IP_NF_NAT_H323) += ip_nat_h323.o + + # connection tracking helpers +obj-$(CONFIG_IP_NF_QUAKE3) += ip_conntrack_quake3.o +ifdef CONFIG_IP_NF_NAT_QUAKE3 + export-objs += ip_conntrack_quake3.o +endif +obj-$(CONFIG_IP_NF_MMS) += ip_conntrack_mms.o +ifdef CONFIG_IP_NF_MMS + export-objs += ip_conntrack_mms.o +endif obj-$(CONFIG_IP_NF_AMANDA) += ip_conntrack_amanda.o obj-$(CONFIG_IP_NF_TFTP) += ip_conntrack_tftp.o obj-$(CONFIG_IP_NF_FTP) += ip_conntrack_ftp.o +obj-$(CONFIG_IP_NF_RSH) += ip_conntrack_rsh.o + obj-$(CONFIG_IP_NF_IRC) += ip_conntrack_irc.o # NAT helpers @@ -30,6 +59,8 @@ obj-$(CONFIG_IP_NF_NAT_TFTP) += ip_nat_tftp.o obj-$(CONFIG_IP_NF_NAT_FTP) += ip_nat_ftp.o obj-$(CONFIG_IP_NF_NAT_IRC) += ip_nat_irc.o +obj-$(CONFIG_IP_NF_NAT_QUAKE3) += ip_nat_quake3.o +obj-$(CONFIG_IP_NF_NAT_MMS) += ip_nat_mms.o # generic IP tables obj-$(CONFIG_IP_NF_IPTABLES) += ip_tables.o @@ -41,6 +72,9 @@ obj-$(CONFIG_IP_NF_RAW) += iptable_raw.o # matches +obj-$(CONFIG_IP_NF_MATCH_RPC) += ip_conntrack_rpc_tcp.o ip_conntrack_rpc_udp.o ipt_rpc.o +export-objs += ip_conntrack_rpc_tcp.o ip_conntrack_rpc_udp.o + obj-$(CONFIG_IP_NF_MATCH_HELPER) += ipt_helper.o obj-$(CONFIG_IP_NF_MATCH_LIMIT) += ipt_limit.o obj-$(CONFIG_IP_NF_MATCH_SCTP) += ipt_sctp.o @@ -87,14 +121,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,18 +141,24 @@ 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_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 +obj-$(CONFIG_IP_NF_TARGET_ROUTE) += ipt_ROUTE.o obj-$(CONFIG_IP_NF_TARGET_NETMAP) += ipt_NETMAP.o obj-$(CONFIG_IP_NF_TARGET_SAME) += ipt_SAME.o 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_TCPLAG) += ipt_TCPLAG.o +obj-$(CONFIG_IP_NF_TARGET_CONNMARK) += ipt_CONNMARK.o obj-$(CONFIG_IP_NF_TARGET_TTL) += ipt_TTL.o obj-$(CONFIG_IP_NF_TARGET_NETLINK) += ipt_NETLINK.o obj-$(CONFIG_IP_NF_TARGET_IPV4OPTSSTRIP) += ipt_IPV4OPTSSTRIP.o obj-$(CONFIG_IP_NF_TARGET_ULOG) += ipt_ULOG.o +obj-$(CONFIG_IP_NF_TARGET_TARPIT) += ipt_TARPIT.o obj-$(CONFIG_IP_NF_TARGET_TCPMSS) += ipt_TCPMSS.o obj-$(CONFIG_IP_NF_TARGET_NOTRACK) += ipt_NOTRACK.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-16 08:59:08.000000000 +0200 +++ linux-2.6.6-rc1/net/ipv4/netfilter/ip_conntrack_core.c 2004-04-16 09:15:41.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_h323.c linux-2.6.6-rc1/net/ipv4/netfilter/ip_conntrack_h323.c --- linux-2.6.6-rc1.org/net/ipv4/netfilter/ip_conntrack_h323.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.6-rc1/net/ipv4/netfilter/ip_conntrack_h323.c 2004-04-16 09:17:05.000000000 +0200 @@ -0,0 +1,308 @@ +/* + * H.323 'brute force' extension for H.323 connection tracking. + * Jozsef Kadlecsik + * + * Based on ip_masq_h323.c for 2.2 kernels from CoRiTel, Sofia project. + * (http://www.coritel.it/projects/sofia/nat/) + * Uses Sampsa Ranta's excellent idea on using expectfn to 'bind' + * the unregistered helpers to the conntrack entries. + */ + + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +MODULE_AUTHOR("Jozsef Kadlecsik "); +MODULE_DESCRIPTION("H.323 'brute force' connection tracking module"); +MODULE_LICENSE("GPL"); + +DECLARE_LOCK(ip_h323_lock); +struct module *ip_conntrack_h323 = THIS_MODULE; + +#if 0 +#define DEBUGP printk +#else +#define DEBUGP(format, args...) +#endif + +/* FIXME: This should be in userspace. Later. */ +static int h245_help(const struct iphdr *iph, size_t len, + struct ip_conntrack *ct, + enum ip_conntrack_info ctinfo) +{ + struct tcphdr *tcph = (void *)iph + iph->ihl * 4; + unsigned char *data = (unsigned char *) tcph + tcph->doff * 4; + unsigned char *data_limit; + u_int32_t tcplen = len - iph->ihl * 4; + u_int32_t datalen = tcplen - tcph->doff * 4; + int dir = CTINFO2DIR(ctinfo); + struct ip_ct_h225_master *info = &ct->help.ct_h225_info; + struct ip_conntrack_expect expect, *exp = &expect; + struct ip_ct_h225_expect *exp_info = &exp->help.exp_h225_info; + u_int16_t data_port; + u_int32_t data_ip; + unsigned int i; + + DEBUGP("ct_h245_help: help entered %u.%u.%u.%u:%u->%u.%u.%u.%u:%u\n", + NIPQUAD(iph->saddr), ntohs(tcph->source), + NIPQUAD(iph->daddr), ntohs(tcph->dest)); + + /* Can't track connections formed before we registered */ + if (!info) + return NF_ACCEPT; + + /* Until there's been traffic both ways, don't look in packets. */ + if (ctinfo != IP_CT_ESTABLISHED + && ctinfo != IP_CT_ESTABLISHED + IP_CT_IS_REPLY) { + DEBUGP("ct_h245_help: Conntrackinfo = %u\n", ctinfo); + return NF_ACCEPT; + } + + /* Not whole TCP header or too short packet? */ + if (tcplen < sizeof(struct tcphdr) || tcplen < tcph->doff * 4 + 5) { + DEBUGP("ct_h245_help: tcplen = %u\n", (unsigned)tcplen); + return NF_ACCEPT; + } + + /* Checksum invalid? Ignore. */ + /* FIXME: Source route IP option packets --RR */ + if (tcp_v4_check(tcph, tcplen, iph->saddr, iph->daddr, + csum_partial((char *)tcph, tcplen, 0))) { + DEBUGP("ct_h245_help: bad csum: %p %u %u.%u.%u.%u %u.%u.%u.%u\n", + tcph, tcplen, NIPQUAD(iph->saddr), + NIPQUAD(iph->daddr)); + return NF_ACCEPT; + } + + data_limit = (unsigned char *) data + datalen; + /* bytes: 0123 45 + ipadrr port */ + for (i = 0; data < (data_limit - 5); data++, i++) { + data_ip = *((u_int32_t *)data); + if (data_ip == iph->saddr) { + data_port = *((u_int16_t *)(data + 4)); + memset(&expect, 0, sizeof(expect)); + /* update the H.225 info */ + DEBUGP("ct_h245_help: new RTCP/RTP requested %u.%u.%u.%u:->%u.%u.%u.%u:%u\n", + NIPQUAD(ct->tuplehash[!dir].tuple.src.ip), + NIPQUAD(iph->saddr), ntohs(data_port)); + LOCK_BH(&ip_h323_lock); + info->is_h225 = H225_PORT + 1; + exp_info->port = data_port; + exp_info->dir = dir; + exp_info->offset = i; + + exp->seq = ntohl(tcph->seq) + i; + + exp->tuple = ((struct ip_conntrack_tuple) + { { ct->tuplehash[!dir].tuple.src.ip, + { 0 } }, + { data_ip, + { .tcp = { data_port } }, + IPPROTO_UDP }}); + exp->mask = ((struct ip_conntrack_tuple) + { { 0xFFFFFFFF, { 0 } }, + { 0xFFFFFFFF, { .tcp = { 0xFFFF } }, 0xFFFF }}); + + exp->expectfn = NULL; + + /* Ignore failure; should only happen with NAT */ + ip_conntrack_expect_related(ct, exp); + + UNLOCK_BH(&ip_h323_lock); + } + } + + return NF_ACCEPT; + +} + +/* H.245 helper is not registered! */ +static struct ip_conntrack_helper h245 = + { { NULL, NULL }, + "H.245", /* name */ + IP_CT_HELPER_F_REUSE_EXPECT, /* flags */ + NULL, /* module */ + 8, /* max_ expected */ + 240, /* timeout */ + { { 0, { 0 } }, /* tuple */ + { 0, { 0 }, IPPROTO_TCP } }, + { { 0, { 0xFFFF } }, /* mask */ + { 0, { 0 }, 0xFFFF } }, + h245_help /* helper */ + }; + +static int h225_expect(struct ip_conntrack *ct) +{ + WRITE_LOCK(&ip_conntrack_lock); + ct->helper = &h245; + DEBUGP("h225_expect: helper for %p added\n", ct); + WRITE_UNLOCK(&ip_conntrack_lock); + + return NF_ACCEPT; /* unused */ +} + +/* FIXME: This should be in userspace. Later. */ +static int h225_help(const struct iphdr *iph, size_t len, + struct ip_conntrack *ct, + enum ip_conntrack_info ctinfo) +{ + struct tcphdr *tcph = (void *)iph + iph->ihl * 4; + unsigned char *data = (unsigned char *) tcph + tcph->doff * 4; + unsigned char *data_limit; + u_int32_t tcplen = len - iph->ihl * 4; + u_int32_t datalen = tcplen - tcph->doff * 4; + int dir = CTINFO2DIR(ctinfo); + struct ip_ct_h225_master *info = &ct->help.ct_h225_info; + struct ip_conntrack_expect expect, *exp = &expect; + struct ip_ct_h225_expect *exp_info = &exp->help.exp_h225_info; + u_int16_t data_port; + u_int32_t data_ip; + unsigned int i; + + DEBUGP("ct_h225_help: help entered %u.%u.%u.%u:%u->%u.%u.%u.%u:%u\n", + NIPQUAD(iph->saddr), ntohs(tcph->source), + NIPQUAD(iph->daddr), ntohs(tcph->dest)); + + /* Can't track connections formed before we registered */ + if (!info) + return NF_ACCEPT; + + /* Until there's been traffic both ways, don't look in packets. */ + if (ctinfo != IP_CT_ESTABLISHED + && ctinfo != IP_CT_ESTABLISHED + IP_CT_IS_REPLY) { + DEBUGP("ct_h225_help: Conntrackinfo = %u\n", ctinfo); + return NF_ACCEPT; + } + + /* Not whole TCP header or too short packet? */ + if (tcplen < sizeof(struct tcphdr) || tcplen < tcph->doff * 4 + 5) { + DEBUGP("ct_h225_help: tcplen = %u\n", (unsigned)tcplen); + return NF_ACCEPT; + } + + /* Checksum invalid? Ignore. */ + /* FIXME: Source route IP option packets --RR */ + if (tcp_v4_check(tcph, tcplen, iph->saddr, iph->daddr, + csum_partial((char *)tcph, tcplen, 0))) { + DEBUGP("ct_h225_help: bad csum: %p %u %u.%u.%u.%u %u.%u.%u.%u\n", + tcph, tcplen, NIPQUAD(iph->saddr), + NIPQUAD(iph->daddr)); + return NF_ACCEPT; + } + + data_limit = (unsigned char *) data + datalen; + /* bytes: 0123 45 + ipadrr port */ + for (i = 0; data < (data_limit - 5); data++, i++) { + data_ip = *((u_int32_t *)data); + if (data_ip == iph->saddr) { + data_port = *((u_int16_t *)(data + 4)); + if (data_port == tcph->source) { + /* Signal address */ + DEBUGP("ct_h225_help: sourceCallSignalAddress from %u.%u.%u.%u\n", + NIPQUAD(iph->saddr)); + /* Update the H.225 info so that NAT can mangle the address/port + even when we have no expected connection! */ +#ifdef CONFIG_IP_NF_NAT_NEEDED + LOCK_BH(&ip_h323_lock); + info->dir = dir; + info->seq[IP_CT_DIR_ORIGINAL] = ntohl(tcph->seq) + i; + info->offset[IP_CT_DIR_ORIGINAL] = i; + UNLOCK_BH(&ip_h323_lock); +#endif + } else { + memset(&expect, 0, sizeof(expect)); + + /* update the H.225 info */ + LOCK_BH(&ip_h323_lock); + info->is_h225 = H225_PORT; + exp_info->port = data_port; + exp_info->dir = dir; + exp_info->offset = i; + + exp->seq = ntohl(tcph->seq) + i; + + exp->tuple = ((struct ip_conntrack_tuple) + { { ct->tuplehash[!dir].tuple.src.ip, + { 0 } }, + { data_ip, + { .tcp = { data_port } }, + IPPROTO_TCP }}); + exp->mask = ((struct ip_conntrack_tuple) + { { 0xFFFFFFFF, { 0 } }, + { 0xFFFFFFFF, { .tcp = { 0xFFFF } }, 0xFFFF }}); + + exp->expectfn = h225_expect; + + /* Ignore failure */ + ip_conntrack_expect_related(ct, exp); + + DEBUGP("ct_h225_help: new H.245 requested %u.%u.%u.%u->%u.%u.%u.%u:%u\n", + NIPQUAD(ct->tuplehash[!dir].tuple.src.ip), + NIPQUAD(iph->saddr), ntohs(data_port)); + + UNLOCK_BH(&ip_h323_lock); + } +#ifdef CONFIG_IP_NF_NAT_NEEDED + } else if (data_ip == iph->daddr) { + data_port = *((u_int16_t *)(data + 4)); + if (data_port == tcph->dest) { + /* Signal address */ + DEBUGP("ct_h225_help: destCallSignalAddress %u.%u.%u.%u\n", + NIPQUAD(iph->daddr)); + /* Update the H.225 info so that NAT can mangle the address/port + even when we have no expected connection! */ + LOCK_BH(&ip_h323_lock); + info->dir = dir; + info->seq[IP_CT_DIR_REPLY] = ntohl(tcph->seq) + i; + info->offset[IP_CT_DIR_REPLY] = i; + UNLOCK_BH(&ip_h323_lock); + } +#endif + } + } + + return NF_ACCEPT; + +} + +static struct ip_conntrack_helper h225 = + { { NULL, NULL }, + "H.225", /* name */ + IP_CT_HELPER_F_REUSE_EXPECT, /* flags */ + THIS_MODULE, /* module */ + 2, /* max_expected */ + 240, /* timeout */ + { { 0, { __constant_htons(H225_PORT) } }, /* tuple */ + { 0, { 0 }, IPPROTO_TCP } }, + { { 0, { 0xFFFF } }, /* mask */ + { 0, { 0 }, 0xFFFF } }, + h225_help /* helper */ + }; + +static int __init init(void) +{ + return ip_conntrack_helper_register(&h225); +} + +static void __exit fini(void) +{ + /* Unregister H.225 helper */ + ip_conntrack_helper_unregister(&h225); +} + +EXPORT_SYMBOL(ip_h323_lock); + +module_init(init); +module_exit(fini); diff -Nur --exclude '*.orig' linux-2.6.6-rc1.org/net/ipv4/netfilter/ip_conntrack_mms.c linux-2.6.6-rc1/net/ipv4/netfilter/ip_conntrack_mms.c --- linux-2.6.6-rc1.org/net/ipv4/netfilter/ip_conntrack_mms.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.6-rc1/net/ipv4/netfilter/ip_conntrack_mms.c 2004-04-16 09:17:32.000000000 +0200 @@ -0,0 +1,308 @@ +/* MMS extension for IP connection tracking + * (C) 2002 by Filip Sneppe + * based on ip_conntrack_ftp.c and ip_conntrack_irc.c + * + * ip_conntrack_mms.c v0.3 2002-09-22 + * + * 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. + * + * Module load syntax: + * insmod ip_conntrack_mms.o ports=port1,port2,...port + * + * Please give the ports of all MMS servers You wish to connect to. + * If you don't specify ports, the default will be TCP port 1755. + * + * More info on MMS protocol, firewalls and NAT: + * http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnwmt/html/MMSFirewall.asp + * http://www.microsoft.com/windows/windowsmedia/serve/firewall.asp + * + * The SDP project people are reverse-engineering MMS: + * http://get.to/sdp + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +DECLARE_LOCK(ip_mms_lock); +struct module *ip_conntrack_mms = THIS_MODULE; + +#define MAX_PORTS 8 +static int ports[MAX_PORTS]; +static int ports_c; +#ifdef MODULE_PARM +MODULE_PARM(ports, "1-" __MODULE_STRING(MAX_PORTS) "i"); +#endif + +#if 0 +#define DEBUGP printk +#else +#define DEBUGP(format, args...) +#endif + +EXPORT_SYMBOL(ip_mms_lock); + +MODULE_AUTHOR("Filip Sneppe "); +MODULE_DESCRIPTION("Microsoft Windows Media Services (MMS) connection tracking module"); +MODULE_LICENSE("GPL"); + +/* #define isdigit(c) (c >= '0' && c <= '9') */ + +/* copied from drivers/usb/serial/io_edgeport.c - not perfect but will do the trick */ +static void unicode_to_ascii (char *string, short *unicode, int unicode_size) +{ + int i; + for (i = 0; i < unicode_size; ++i) { + string[i] = (char)(unicode[i]); + } + string[unicode_size] = 0x00; +} + +__inline static int atoi(char *s) +{ + int i=0; + while (isdigit(*s)) { + i = i*10 + *(s++) - '0'; + } + return i; +} + +/* convert ip address string like "192.168.0.10" to unsigned int */ +__inline static u_int32_t asciiiptoi(char *s) +{ + unsigned int i, j, k; + + for(i=k=0; k<3; ++k, ++s, i<<=8) { + i+=atoi(s); + for(j=0; (*(++s) != '.') && (j<3); ++j) + ; + } + i+=atoi(s); + return ntohl(i); +} + +int parse_mms(const char *data, + const unsigned int datalen, + u_int32_t *mms_ip, + u_int16_t *mms_proto, + u_int16_t *mms_port, + char **mms_string_b, + char **mms_string_e, + char **mms_padding_e) +{ + int unicode_size, i; + char tempstring[28]; /* "\\255.255.255.255\UDP\65535" */ + char getlengthstring[28]; + + for(unicode_size=0; + (char) *(data+(MMS_SRV_UNICODE_STRING_OFFSET+unicode_size*2)) != (char)0; + unicode_size++) + if ((unicode_size == 28) || (MMS_SRV_UNICODE_STRING_OFFSET+unicode_size*2 >= datalen)) + return -1; /* out of bounds - incomplete packet */ + + unicode_to_ascii(tempstring, (short *)(data+MMS_SRV_UNICODE_STRING_OFFSET), unicode_size); + DEBUGP("ip_conntrack_mms: offset 60: %s\n", (const char *)(tempstring)); + + /* IP address ? */ + *mms_ip = asciiiptoi(tempstring+2); + + i=sprintf(getlengthstring, "%u.%u.%u.%u", HIPQUAD(*mms_ip)); + + /* protocol ? */ + if(strncmp(tempstring+3+i, "TCP", 3)==0) + *mms_proto = IPPROTO_TCP; + else if(strncmp(tempstring+3+i, "UDP", 3)==0) + *mms_proto = IPPROTO_UDP; + + /* port ? */ + *mms_port = atoi(tempstring+7+i); + + /* we store a pointer to the beginning of the "\\a.b.c.d\proto\port" + unicode string, one to the end of the string, and one to the end + of the packet, since we must keep track of the number of bytes + between end of the unicode string and the end of packet (padding) */ + *mms_string_b = (char *)(data + MMS_SRV_UNICODE_STRING_OFFSET); + *mms_string_e = (char *)(data + MMS_SRV_UNICODE_STRING_OFFSET + unicode_size * 2); + *mms_padding_e = (char *)(data + datalen); /* looks funny, doesn't it */ + return 0; +} + + +/* FIXME: This should be in userspace. Later. */ +static int help(const struct iphdr *iph, size_t len, + struct ip_conntrack *ct, + enum ip_conntrack_info ctinfo) +{ + /* tcplen not negative guaranteed by ip_conntrack_tcp.c */ + struct tcphdr *tcph = (void *)iph + iph->ihl * 4; + const char *data = (const char *)tcph + tcph->doff * 4; + unsigned int tcplen = len - iph->ihl * 4; + unsigned int datalen = tcplen - tcph->doff * 4; + int dir = CTINFO2DIR(ctinfo); + struct ip_conntrack_expect expect, *exp = &expect; + struct ip_ct_mms_expect *exp_mms_info = &exp->help.exp_mms_info; + + u_int32_t mms_ip; + u_int16_t mms_proto; + char mms_proto_string[8]; + u_int16_t mms_port; + char *mms_string_b, *mms_string_e, *mms_padding_e; + + /* Until there's been traffic both ways, don't look in packets. */ + if (ctinfo != IP_CT_ESTABLISHED + && ctinfo != IP_CT_ESTABLISHED+IP_CT_IS_REPLY) { + DEBUGP("ip_conntrack_mms: Conntrackinfo = %u\n", ctinfo); + return NF_ACCEPT; + } + + /* Not whole TCP header? */ + if (tcplen < sizeof(struct tcphdr) || tcplen < tcph->doff*4) { + DEBUGP("ip_conntrack_mms: tcplen = %u\n", (unsigned)tcplen); + return NF_ACCEPT; + } + + /* Checksum invalid? Ignore. */ + /* FIXME: Source route IP option packets --RR */ + if (tcp_v4_check(tcph, tcplen, iph->saddr, iph->daddr, + csum_partial((char *)tcph, tcplen, 0))) { + DEBUGP("mms_help: bad csum: %p %u %u.%u.%u.%u %u.%u.%u.%u\n", + tcph, tcplen, NIPQUAD(iph->saddr), + NIPQUAD(iph->daddr)); + return NF_ACCEPT; + } + + /* Only look at packets with 0x00030002/196610 on bytes 36->39 of TCP payload */ + /* FIXME: There is an issue with only looking at this packet: before this packet, + the client has already sent a packet to the server with the server's hostname + according to the client (think of it as the "Host: " header in HTTP/1.1). The + server will break the connection if this doesn't correspond to its own host + header. The client can also connect to an IP address; if it's the server's IP + address, it will not break the connection. When doing DNAT on a connection + where the client uses a server's IP address, the nat module should detect + this and change this string accordingly to the DNATed address. This should + probably be done by checking for an IP address, then storing it as a member + of struct ip_ct_mms_expect and checking for it in ip_nat_mms... + */ + if( (MMS_SRV_MSG_OFFSET < datalen) && + ((*(u32 *)(data+MMS_SRV_MSG_OFFSET)) == MMS_SRV_MSG_ID)) { + DEBUGP("ip_conntrack_mms: offset 37: %u %u %u %u, datalen:%u\n", + (u8)*(data+36), (u8)*(data+37), + (u8)*(data+38), (u8)*(data+39), + datalen); + if(parse_mms(data, datalen, &mms_ip, &mms_proto, &mms_port, + &mms_string_b, &mms_string_e, &mms_padding_e)) + if(net_ratelimit()) + /* FIXME: more verbose debugging ? */ + printk(KERN_WARNING + "ip_conntrack_mms: Unable to parse data payload\n"); + + memset(&expect, 0, sizeof(expect)); + + sprintf(mms_proto_string, "(%u)", mms_proto); + DEBUGP("ip_conntrack_mms: adding %s expectation %u.%u.%u.%u -> %u.%u.%u.%u:%u\n", + mms_proto == IPPROTO_TCP ? "TCP" + : mms_proto == IPPROTO_UDP ? "UDP":mms_proto_string, + NIPQUAD(ct->tuplehash[!dir].tuple.src.ip), + NIPQUAD(mms_ip), + mms_port); + + /* it's possible that the client will just ask the server to tunnel + the stream over the same TCP session (from port 1755): there's + shouldn't be a need to add an expectation in that case, but it + makes NAT packet mangling so much easier */ + LOCK_BH(&ip_mms_lock); + + DEBUGP("ip_conntrack_mms: tcph->seq = %u\n", tcph->seq); + + exp->seq = ntohl(tcph->seq) + (mms_string_b - data); + exp_mms_info->len = (mms_string_e - mms_string_b); + exp_mms_info->padding = (mms_padding_e - mms_string_e); + exp_mms_info->port = mms_port; + + DEBUGP("ip_conntrack_mms: wrote info seq=%u (ofs=%u), len=%d, padding=%u\n", + exp->seq, (mms_string_e - data), exp_mms_info->len, exp_mms_info->padding); + + exp->tuple = ((struct ip_conntrack_tuple) + { { ct->tuplehash[!dir].tuple.src.ip, { 0 } }, + { mms_ip, + { .tcp = { (__u16) ntohs(mms_port) } }, + mms_proto } } + ); + exp->mask = ((struct ip_conntrack_tuple) + { { 0xFFFFFFFF, { 0 } }, + { 0xFFFFFFFF, { .tcp = { 0xFFFF } }, 0xFFFF }}); + exp->expectfn = NULL; + ip_conntrack_expect_related(ct, &expect); + UNLOCK_BH(&ip_mms_lock); + } + + return NF_ACCEPT; +} + +static struct ip_conntrack_helper mms[MAX_PORTS]; +static char mms_names[MAX_PORTS][10]; + +/* Not __exit: called from init() */ +static void fini(void) +{ + int i; + for (i = 0; (i < MAX_PORTS) && ports[i]; i++) { + DEBUGP("ip_conntrack_mms: unregistering helper for port %d\n", + ports[i]); + ip_conntrack_helper_unregister(&mms[i]); + } +} + +static int __init init(void) +{ + int i, ret; + char *tmpname; + + if (ports[0] == 0) + ports[0] = MMS_PORT; + + for (i = 0; (i < MAX_PORTS) && ports[i]; i++) { + memset(&mms[i], 0, sizeof(struct ip_conntrack_helper)); + mms[i].tuple.src.u.tcp.port = htons(ports[i]); + mms[i].tuple.dst.protonum = IPPROTO_TCP; + mms[i].mask.src.u.tcp.port = 0xFFFF; + mms[i].mask.dst.protonum = 0xFFFF; + mms[i].max_expected = 1; + mms[i].timeout = 0; + mms[i].flags = IP_CT_HELPER_F_REUSE_EXPECT; + mms[i].me = THIS_MODULE; + mms[i].help = help; + + tmpname = &mms_names[i][0]; + if (ports[i] == MMS_PORT) + sprintf(tmpname, "mms"); + else + sprintf(tmpname, "mms-%d", ports[i]); + mms[i].name = tmpname; + + DEBUGP("ip_conntrack_mms: registering helper for port %d\n", + ports[i]); + ret = ip_conntrack_helper_register(&mms[i]); + + if (ret) { + fini(); + return ret; + } + ports_c++; + } + return 0; +} + +module_init(init); +module_exit(fini); 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-16 09:18:47.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_quake3.c linux-2.6.6-rc1/net/ipv4/netfilter/ip_conntrack_quake3.c --- linux-2.6.6-rc1.org/net/ipv4/netfilter/ip_conntrack_quake3.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.6-rc1/net/ipv4/netfilter/ip_conntrack_quake3.c 2004-04-16 09:18:43.000000000 +0200 @@ -0,0 +1,156 @@ +/* Quake3 extension for IP connection tracking + * (C) 2002 by Filip Sneppe + * based on ip_conntrack_ftp.c and ip_conntrack_tftp.c + * + * ip_conntrack_quake3.c v0.04 2002-08-31 + * + * 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. + * + * Module load syntax: + * insmod ip_conntrack_quake3.o ports=port1,port2,...port + * + * please give the ports of all Quake3 master servers You wish to + * connect to. If you don't specify ports, the default will be UDP + * port 27950. + * + * Thanks to the Ethereal folks for their analysis of the Quake3 protocol. + */ + +#include +#include +#include + +#include +#include +#include +#include + +struct module *ip_conntrack_quake3 = THIS_MODULE; + +MODULE_AUTHOR("Filip Sneppe "); +MODULE_DESCRIPTION("Netfilter connection tracking module for Quake III Arena"); +MODULE_LICENSE("GPL"); + +#define MAX_PORTS 8 +static int ports[MAX_PORTS]; +static int ports_c = 0; +#ifdef MODULE_PARM +MODULE_PARM(ports, "1-" __MODULE_STRING(MAX_PORTS) "i"); +MODULE_PARM_DESC(ports, "port numbers of Quake III master servers"); +#endif + +/* Quake3 master server reply will add > 100 expectations per reply packet; when + doing lots of printk's, klogd may not be able to read /proc/kmsg fast enough */ +#if 0 +#define DEBUGP printk +#else +#define DEBUGP(format, args...) +#endif + +struct quake3_search quake3s_conntrack = { "****", "getserversResponse", sizeof("getserversResponse") - 1 }; + +static int quake3_help(const struct iphdr *iph, size_t len, + struct ip_conntrack *ct, + enum ip_conntrack_info ctinfo) +{ + struct udphdr *udph = (void *)iph + iph->ihl * 4; + int dir = CTINFO2DIR(ctinfo); + struct ip_conntrack_expect exp; + int i; + + /* Until there's been traffic both ways, don't look in packets. note: it's UDP ! */ + if (ctinfo != IP_CT_ESTABLISHED + && ctinfo != IP_CT_IS_REPLY) { + DEBUGP("ip_conntrack_quake3: not ok ! Conntrackinfo = %u\n", ctinfo); + return NF_ACCEPT; + } else { DEBUGP("ip_conntrack_quake3: it's ok ! Conntrackinfo = %u\n", ctinfo); } + + if (strnicmp((const char *)udph + 12, quake3s_conntrack.pattern, quake3s_conntrack.plen) == 0) { + for(i=31; /* 8 bytes UDP hdr, 4 bytes filler, 18 bytes "getserversResponse", 1 byte "\" */ + i+6 < ntohs(udph->len); + i+=7) { + DEBUGP("ip_conntrack_quake3: adding server at offset %u/%u %u.%u.%u.%u:%u\n", + i, ntohs(udph->len), + NIPQUAD( (u_int32_t) *( (u_int32_t *)( (int)udph + i ) ) ), + ntohs((__u16) *( (__u16 *)( (int)udph + i + 4 ) ) ) ); + + memset(&exp, 0, sizeof(exp)); + + exp.tuple = ((struct ip_conntrack_tuple) + { { ct->tuplehash[!dir].tuple.src.ip, { 0 } }, + { (u_int32_t) *((u_int32_t *)((int)udph + i)), + { .udp = { (__u16) *((__u16 *)((int)udph+i+4)) } }, + IPPROTO_UDP } } + ); + exp.mask = ((struct ip_conntrack_tuple) + { { 0xFFFFFFFF, { 0 } }, + { 0xFFFFFFFF, { .udp = { 0xFFFF } }, 0xFFFF }}); + exp.expectfn = NULL; + + ip_conntrack_expect_related(ct, &exp); + } + + } + + return(NF_ACCEPT); +} + +static struct ip_conntrack_helper quake3[MAX_PORTS]; +static char quake3_names[MAX_PORTS][13]; /* quake3-65535 */ + +static void fini(void) +{ + int i; + + for(i = 0 ; (i < ports_c); i++) { + DEBUGP("ip_conntrack_quake3: unregistering helper for port %d\n", + ports[i]); + ip_conntrack_helper_unregister(&quake3[i]); + } +} + +static int __init init(void) +{ + int i, ret; + char *tmpname; + + if(!ports[0]) + ports[0]=QUAKE3_MASTER_PORT; + + for(i = 0 ; (i < MAX_PORTS) && ports[i] ; i++) { + /* Create helper structure */ + memset(&quake3[i], 0, sizeof(struct ip_conntrack_helper)); + + quake3[i].tuple.dst.protonum = IPPROTO_UDP; + quake3[i].tuple.src.u.udp.port = htons(ports[i]); + quake3[i].mask.dst.protonum = 0xFFFF; + quake3[i].mask.src.u.udp.port = 0xFFFF; + quake3[i].help = quake3_help; + quake3[i].me = THIS_MODULE; + + tmpname = &quake3_names[i][0]; + if (ports[i] == QUAKE3_MASTER_PORT) + sprintf(tmpname, "quake3"); + else + sprintf(tmpname, "quake3-%d", i); + quake3[i].name = tmpname; + + DEBUGP("ip_conntrack_quake3: registering helper for port %d\n", + ports[i]); + + ret=ip_conntrack_helper_register(&quake3[i]); + if(ret) { + fini(); + return(ret); + } + ports_c++; + } + + return(0); +} + +module_init(init); +module_exit(fini); diff -Nur --exclude '*.orig' linux-2.6.6-rc1.org/net/ipv4/netfilter/ip_conntrack_rpc_tcp.c linux-2.6.6-rc1/net/ipv4/netfilter/ip_conntrack_rpc_tcp.c --- linux-2.6.6-rc1.org/net/ipv4/netfilter/ip_conntrack_rpc_tcp.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.6-rc1/net/ipv4/netfilter/ip_conntrack_rpc_tcp.c 2004-04-16 09:18:44.000000000 +0200 @@ -0,0 +1,508 @@ +/* RPC extension for IP (TCP) connection tracking, Version 2.2 + * (C) 2000 by Marcelo Barbosa Lima + * - original rpc tracking module + * - "recent" connection handling for kernel 2.3+ netfilter + * + * (C) 2001 by Rusty Russell + * - upgraded conntrack modules to oldnat api - kernel 2.4.0+ + * + * (C) 2002,2003 by Ian (Larry) Latter + * - upgraded conntrack modules to newnat api - kernel 2.4.20+ + * - extended matching to support filtering on procedures + * + * ip_conntrack_rpc_tpc.c,v 2.2 2003/01/12 18:30:00 + * + * 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. + ** + * Module load syntax: + * insmod ip_conntrack_rpc_tcp.o ports=port1,port2,...port + * + * Please give the ports of all RPC servers you wish to connect to. + * If you don't specify ports, the default will be port 111. + ** + * Note to all: + * + * RPCs should not be exposed to the internet - ask the Pentagon; + * + * "The unidentified crackers pleaded guilty in July to charges + * of juvenile delinquency stemming from a string of Pentagon + * network intrusions in February. + * + * The youths, going by the names TooShort and Makaveli, used + * a common server security hole to break in, according to + * Dane Jasper, owner of the California Internet service + * provider, Sonic. They used the hole, known as the 'statd' + * exploit, to attempt more than 800 break-ins, Jasper said." + * + * From: Wired News; "Pentagon Kids Kicked Off Grid" - Nov 6, 1998 + * URL: http://www.wired.com/news/politics/0,1283,16098,00.html + ** + */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#define MAX_PORTS 8 +static int ports[MAX_PORTS]; +static int ports_n_c = 0; + +#ifdef MODULE_PARM +MODULE_PARM(ports, "1-" __MODULE_STRING(MAX_PORTS) "i"); +MODULE_PARM_DESC(ports, "port numbers (TCP/UDP) of RPC portmapper servers"); +#endif + +MODULE_AUTHOR("Marcelo Barbosa Lima "); +MODULE_DESCRIPTION("RPC TCP connection tracking module"); +MODULE_LICENSE("GPL"); + +#if 0 +#define DEBUGP(format, args...) printk(KERN_DEBUG "ip_conntrack_rpc_tcp: " \ + format, ## args) +#else +#define DEBUGP(format, args...) +#endif + +DECLARE_RWLOCK(ipct_rpc_tcp_lock); +#define ASSERT_READ_LOCK(x) MUST_BE_READ_LOCKED(&ipct_rpc_tcp_lock) +#define ASSERT_WRITE_LOCK(x) MUST_BE_WRITE_LOCKED(&ipct_rpc_tcp_lock) +#include + +/* For future conections RPC, using client's cache bindings + * I'll use ip_conntrack_lock to lock these lists */ + +LIST_HEAD(request_p_list_tcp); + + +static void delete_request_p(unsigned long request_p_ul) +{ + struct request_p *p = (void *)request_p_ul; + + WRITE_LOCK(&ipct_rpc_tcp_lock); + LIST_DELETE(&request_p_list_tcp, p); + WRITE_UNLOCK(&ipct_rpc_tcp_lock); + kfree(p); + return; +} + + +static void req_cl(struct request_p * r) +{ + WRITE_LOCK(&ipct_rpc_tcp_lock); + del_timer(&r->timeout); + LIST_DELETE(&request_p_list_tcp, r); + WRITE_UNLOCK(&ipct_rpc_tcp_lock); + kfree(r); + return; +} + + +static void clean_request(struct list_head *list) +{ + struct list_head *first = list->prev; + struct list_head *temp = list->next; + struct list_head *aux; + + if (list_empty(list)) + return; + + while (first != temp) { + aux = temp->next; + req_cl((struct request_p *)temp); + temp = aux; + } + req_cl((struct request_p *)temp); + return; +} + + +static void alloc_request_p(u_int32_t xid, u_int16_t proto, u_int32_t ip, + u_int16_t port) +{ + struct request_p *req_p; + + /* Verifies if entry already exists */ + WRITE_LOCK(&ipct_rpc_tcp_lock); + req_p = LIST_FIND(&request_p_list_tcp, request_p_cmp, + struct request_p *, xid, ip, port); + + if (req_p) { + /* Refresh timeout */ + if (del_timer(&req_p->timeout)) { + req_p->timeout.expires = jiffies + EXP; + add_timer(&req_p->timeout); + } + WRITE_UNLOCK(&ipct_rpc_tcp_lock); + return; + + } + WRITE_UNLOCK(&ipct_rpc_tcp_lock); + + /* Allocate new request_p */ + req_p = (struct request_p *) kmalloc(sizeof(struct request_p), GFP_ATOMIC); + if (!req_p) { + DEBUGP("can't allocate request_p\n"); + return; + } + *req_p = ((struct request_p) {{ NULL, NULL }, xid, ip, port, proto, + { { NULL, NULL }, jiffies + EXP, (unsigned long)req_p, + NULL }}); + + /* Initialize timer */ + init_timer(&req_p->timeout); + req_p->timeout.function = delete_request_p; + add_timer(&req_p->timeout); + + /* Put in list */ + WRITE_LOCK(&ipct_rpc_tcp_lock); + list_prepend(&request_p_list_tcp, req_p); + WRITE_UNLOCK(&ipct_rpc_tcp_lock); + return; + +} + + +static int check_rpc_packet(const u_int32_t *data, + int dir, struct ip_conntrack *ct, + struct list_head request_p_list) +{ + struct request_p *req_p; + u_int32_t xid; + struct ip_conntrack_expect expect, *exp = &expect; + + /* Translstion's buffer for XDR */ + u_int16_t port_buf; + + + /* Get XID */ + xid = *data; + + /* This does sanity checking on RPC payloads, + * and permits only the RPC "get port" (3) + * in authorised procedures in client + * communications with the portmapper. + */ + + /* perform direction dependant RPC work */ + if (dir == IP_CT_DIR_ORIGINAL) { + + data += 5; + + /* Get RPC requestor */ + if (IXDR_GET_INT32(data) != 3) { + DEBUGP("RPC packet contains an invalid (non \"get\") requestor. [skip]\n"); + return NF_ACCEPT; + } + DEBUGP("RPC packet contains a \"get\" requestor. [cont]\n"); + + data++; + + /* Jump Credentials and Verfifier */ + data += IXDR_GET_INT32(data) + 2; + data += IXDR_GET_INT32(data) + 2; + + /* Get RPC procedure */ + DEBUGP("RPC packet contains procedure request [%u]. [cont]\n", + (unsigned int)IXDR_GET_INT32(data)); + + /* Get RPC protocol and store against client parameters */ + data = data + 2; + alloc_request_p(xid, IXDR_GET_INT32(data), ct->tuplehash[dir].tuple.src.ip, + ct->tuplehash[dir].tuple.src.u.all); + + DEBUGP("allocated RPC req_p for xid=%u proto=%u %u.%u.%u.%u:%u\n", + xid, IXDR_GET_INT32(data), + NIPQUAD(ct->tuplehash[dir].tuple.src.ip), + ntohs(ct->tuplehash[dir].tuple.src.u.all)); + + DEBUGP("allocated RPC request for protocol %u. [done]\n", + (unsigned int)IXDR_GET_INT32(data)); + + } else { + + /* Check for returning packet's stored counterpart */ + req_p = LIST_FIND(&request_p_list_tcp, request_p_cmp, + struct request_p *, xid, + ct->tuplehash[!dir].tuple.src.ip, + ct->tuplehash[!dir].tuple.src.u.all); + + /* Drop unexpected packets */ + if (!req_p) { + DEBUGP("packet is not expected. [skip]\n"); + return NF_ACCEPT; + } + + /* Verifies if packet is really an RPC reply packet */ + data = data++; + if (IXDR_GET_INT32(data) != 1) { + DEBUGP("packet is not a valid RPC reply. [skip]\n"); + return NF_ACCEPT; + } + + /* Is status accept? */ + data++; + if (IXDR_GET_INT32(data)) { + DEBUGP("packet is not an RPC accept. [skip]\n"); + return NF_ACCEPT; + } + + /* Get Verifier length. Jump verifier */ + data++; + data = data + IXDR_GET_INT32(data) + 2; + + /* Is accpet status "success"? */ + if (IXDR_GET_INT32(data)) { + DEBUGP("packet is not an RPC accept status of success. [skip]\n"); + return NF_ACCEPT; + } + + /* Get server port number */ + data++; + port_buf = (u_int16_t) IXDR_GET_INT32(data); + + /* If a packet has made it this far then it deserves an + * expectation ... if port == 0, then this service is + * not going to be registered. + */ + if (port_buf) { + DEBUGP("port found: %u\n", port_buf); + + memset(&expect, 0, sizeof(expect)); + + /* Watch out, Radioactive-Man! */ + exp->tuple.src.ip = ct->tuplehash[!dir].tuple.src.ip; + exp->tuple.dst.ip = ct->tuplehash[!dir].tuple.dst.ip; + exp->mask.src.ip = 0xffffffff; + exp->mask.dst.ip = 0xffffffff; + + switch (req_p->proto) { + case IPPROTO_UDP: + exp->tuple.src.u.udp.port = 0; + exp->tuple.dst.u.udp.port = htons(port_buf); + exp->tuple.dst.protonum = IPPROTO_UDP; + exp->mask.src.u.udp.port = 0; + exp->mask.dst.u.udp.port = htons(0xffff); + exp->mask.dst.protonum = 0xffff; + break; + + case IPPROTO_TCP: + exp->tuple.src.u.tcp.port = 0; + exp->tuple.dst.u.tcp.port = htons(port_buf); + exp->tuple.dst.protonum = IPPROTO_TCP; + exp->mask.src.u.tcp.port = 0; + exp->mask.dst.u.tcp.port = htons(0xffff); + exp->mask.dst.protonum = 0xffff; + break; + } + exp->expectfn = NULL; + + ip_conntrack_expect_related(ct, &expect); + + DEBUGP("expect related ip %u.%u.%u.%u:0-%u.%u.%u.%u:%u proto=%u\n", + NIPQUAD(exp->tuple.src.ip), + NIPQUAD(exp->tuple.dst.ip), + port_buf, req_p->proto); + + DEBUGP("expect related mask %u.%u.%u.%u:0-%u.%u.%u.%u:65535 proto=%u\n", + NIPQUAD(exp->mask.src.ip), + NIPQUAD(exp->mask.dst.ip), + exp->mask.dst.protonum); + + } + + req_cl(req_p); + + DEBUGP("packet evaluated. [expect]\n"); + return NF_ACCEPT; + } + + return NF_ACCEPT; + +} + + +/* RPC TCP helper */ +static int help(const struct iphdr *iph, size_t len, + struct ip_conntrack *ct, enum ip_conntrack_info ctinfo) +{ + struct tcphdr *tcph = (void *) iph + iph->ihl * 4; + const u_int32_t *data = (const u_int32_t *)tcph + tcph->doff; + size_t tcplen = len - iph->ihl * 4; + + int dir = CTINFO2DIR(ctinfo); + int crp_ret; + + + DEBUGP("new packet to evaluate ..\n"); + + /* This works for packets like handshake packets, ignore */ + if (len == ((tcph->doff + iph->ihl) * 4)) { + DEBUGP("packet has no data (may still be handshaking). [skip]\n"); + return NF_ACCEPT; + } + + /* Until there's been traffic both ways, don't look in packets. */ + if (ctinfo != IP_CT_ESTABLISHED + && ctinfo != IP_CT_ESTABLISHED + IP_CT_IS_REPLY) { + DEBUGP("connection tracking state is; ctinfo=%u ..\n", ctinfo); + DEBUGP("[note: failure to get past this error may indicate asymmetric routing]\n"); + DEBUGP("packet is not yet part of a two way stream. [skip]\n"); + return NF_ACCEPT; + } + + /* Not whole TCP header? */ + if (tcplen < sizeof(struct tcphdr) || tcplen < tcph->doff * 4) { + DEBUGP("TCP header length is; tcplen=%u ..\n", (unsigned) tcplen); + DEBUGP("packet does not contain a complete TCP header. [skip]\n"); + return NF_ACCEPT; + } + + /* FIXME: Source route IP option packets --RR */ + if (tcp_v4_check(tcph, tcplen, iph->saddr, iph->daddr, + csum_partial((char *) tcph, tcplen, 0))) { + DEBUGP("csum; %p %u %u.%u.%u.%u %u.%u.%u.%u\n", + tcph, tcplen, NIPQUAD(iph->saddr), + NIPQUAD(iph->daddr)); + DEBUGP("[note: failure to get past this error may indicate source routing]\n"); + DEBUGP("packet contains a bad checksum. [skip]\n"); + return NF_ACCEPT; + } + + /* perform direction dependant protocol work */ + if (dir == IP_CT_DIR_ORIGINAL) { + + DEBUGP("packet is from the initiator. [cont]\n"); + + /* Tests if packet len is ok */ + if ((tcplen - (tcph->doff * 4)) != 60) { + DEBUGP("packet length is not correct. [skip]\n"); + return NF_ACCEPT; + } + + } else { + + DEBUGP("packet is from the receiver. [cont]\n"); + + /* Tests if packet len is ok */ + if ((tcplen - (tcph->doff * 4)) != 32) { + DEBUGP("packet length is not correct. [skip]\n"); + return NF_ACCEPT; + } + } + + /* Get to the data */ + data++; + + /* Check the RPC data */ + crp_ret = check_rpc_packet(data, dir, ct, request_p_list_tcp); + + return crp_ret; + +} + + +static struct ip_conntrack_helper rpc_helpers[MAX_PORTS]; + +static void fini(void); + + +static int __init init(void) +{ + int port, ret; + static char name[10]; + + + /* If no port given, default to standard RPC port */ + if (ports[0] == 0) + ports[0] = RPC_PORT; + + for (port = 0; (port < MAX_PORTS) && ports[port]; port++) { + memset(&rpc_helpers[port], 0, sizeof(struct ip_conntrack_helper)); + + if (ports[port] == RPC_PORT) + sprintf(name, "rpc"); + else + sprintf(name, "rpc-%d", port); + + rpc_helpers[port].name = name; + rpc_helpers[port].me = THIS_MODULE; + rpc_helpers[port].max_expected = 1; + rpc_helpers[port].flags = IP_CT_HELPER_F_REUSE_EXPECT; + rpc_helpers[port].timeout = 0; + + rpc_helpers[port].tuple.dst.protonum = IPPROTO_TCP; + rpc_helpers[port].mask.dst.protonum = 0xffff; + + /* RPC can come from ports 0:65535 to ports[port] (111) */ + rpc_helpers[port].tuple.src.u.udp.port = htons(ports[port]); + rpc_helpers[port].mask.src.u.udp.port = htons(0xffff); + rpc_helpers[port].mask.dst.u.udp.port = htons(0x0); + + rpc_helpers[port].help = help; + + DEBUGP("registering helper for port #%d: %d/TCP\n", port, ports[port]); + DEBUGP("helper match ip %u.%u.%u.%u:%u->%u.%u.%u.%u:%u\n", + NIPQUAD(rpc_helpers[port].tuple.dst.ip), + ntohs(rpc_helpers[port].tuple.dst.u.tcp.port), + NIPQUAD(rpc_helpers[port].tuple.src.ip), + ntohs(rpc_helpers[port].tuple.src.u.tcp.port)); + DEBUGP("helper match mask %u.%u.%u.%u:%u->%u.%u.%u.%u:%u\n", + NIPQUAD(rpc_helpers[port].mask.dst.ip), + ntohs(rpc_helpers[port].mask.dst.u.tcp.port), + NIPQUAD(rpc_helpers[port].mask.src.ip), + ntohs(rpc_helpers[port].mask.src.u.tcp.port)); + + ret = ip_conntrack_helper_register(&rpc_helpers[port]); + + if (ret) { + printk("ERROR registering port %d\n", + ports[port]); + fini(); + return -EBUSY; + } + ports_n_c++; + } + return 0; +} + + +/* This function is intentionally _NOT_ defined as __exit, because + * it is needed by the init function */ +static void fini(void) +{ + int port; + + DEBUGP("cleaning request list\n"); + clean_request(&request_p_list_tcp); + + for (port = 0; (port < ports_n_c) && ports[port]; port++) { + DEBUGP("unregistering port %d\n", ports[port]); + ip_conntrack_helper_unregister(&rpc_helpers[port]); + } +} + + +module_init(init); +module_exit(fini); + +struct module *ip_conntrack_rpc_tcp = THIS_MODULE; +EXPORT_SYMBOL(request_p_list_tcp); +EXPORT_SYMBOL(ip_conntrack_rpc_tcp); +EXPORT_SYMBOL(ipct_rpc_tcp_lock); + diff -Nur --exclude '*.orig' linux-2.6.6-rc1.org/net/ipv4/netfilter/ip_conntrack_rpc_udp.c linux-2.6.6-rc1/net/ipv4/netfilter/ip_conntrack_rpc_udp.c --- linux-2.6.6-rc1.org/net/ipv4/netfilter/ip_conntrack_rpc_udp.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.6-rc1/net/ipv4/netfilter/ip_conntrack_rpc_udp.c 2004-04-16 09:18:44.000000000 +0200 @@ -0,0 +1,503 @@ +/* RPC extension for IP (UDP) connection tracking, Version 2.2 + * (C) 2000 by Marcelo Barbosa Lima + * - original rpc tracking module + * - "recent" connection handling for kernel 2.3+ netfilter + * + * (C) 2001 by Rusty Russell + * - upgraded conntrack modules to oldnat api - kernel 2.4.0+ + * + * (C) 2002,2003 by Ian (Larry) Latter + * - upgraded conntrack modules to newnat api - kernel 2.4.20+ + * - extended matching to support filtering on procedures + * + * ip_conntrack_rpc_udp.c,v 2.2 2003/01/12 18:30:00 + * + * 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. + ** + * Module load syntax: + * insmod ip_conntrack_rpc_udp.o ports=port1,port2,...port + * + * Please give the ports of all RPC servers you wish to connect to. + * If you don't specify ports, the default will be port 111. + ** + * Note to all: + * + * RPCs should not be exposed to the internet - ask the Pentagon; + * + * "The unidentified crackers pleaded guilty in July to charges + * of juvenile delinquency stemming from a string of Pentagon + * network intrusions in February. + * + * The youths, going by the names TooShort and Makaveli, used + * a common server security hole to break in, according to + * Dane Jasper, owner of the California Internet service + * provider, Sonic. They used the hole, known as the 'statd' + * exploit, to attempt more than 800 break-ins, Jasper said." + * + * From: Wired News; "Pentagon Kids Kicked Off Grid" - Nov 6, 1998 + * URL: http://www.wired.com/news/politics/0,1283,16098,00.html + ** + */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#define MAX_PORTS 8 +static int ports[MAX_PORTS]; +static int ports_n_c = 0; + +#ifdef MODULE_PARM +MODULE_PARM(ports, "1-" __MODULE_STRING(MAX_PORTS) "i"); +MODULE_PARM_DESC(ports, "port numbers (TCP/UDP) of RPC portmapper servers"); +#endif + +MODULE_AUTHOR("Marcelo Barbosa Lima "); +MODULE_DESCRIPTION("RPC UDP connection tracking module"); +MODULE_LICENSE("GPL"); + +#if 0 +#define DEBUGP(format, args...) printk(KERN_DEBUG "ip_conntrack_rpc_udp: " \ + format, ## args) +#else +#define DEBUGP(format, args...) +#endif + +DECLARE_RWLOCK(ipct_rpc_udp_lock); +#define ASSERT_READ_LOCK(x) MUST_BE_READ_LOCKED(&ipct_rpc_udp_lock) +#define ASSERT_WRITE_LOCK(x) MUST_BE_WRITE_LOCKED(&ipct_rpc_udp_lock) +#include + +/* For future conections RPC, using client's cache bindings + * I'll use ip_conntrack_lock to lock these lists */ + +LIST_HEAD(request_p_list_udp); + + +static void delete_request_p(unsigned long request_p_ul) +{ + struct request_p *p = (void *)request_p_ul; + + WRITE_LOCK(&ipct_rpc_udp_lock); + LIST_DELETE(&request_p_list_udp, p); + WRITE_UNLOCK(&ipct_rpc_udp_lock); + kfree(p); + return; +} + + +static void req_cl(struct request_p * r) +{ + WRITE_LOCK(&ipct_rpc_udp_lock); + del_timer(&r->timeout); + LIST_DELETE(&request_p_list_udp, r); + WRITE_UNLOCK(&ipct_rpc_udp_lock); + kfree(r); + return; +} + + +static void clean_request(struct list_head *list) +{ + struct list_head *first = list->prev; + struct list_head *temp = list->next; + struct list_head *aux; + + if (list_empty(list)) + return; + + while (first != temp) { + aux = temp->next; + req_cl((struct request_p *)temp); + temp = aux; + } + req_cl((struct request_p *)temp); + return; +} + + +static void alloc_request_p(u_int32_t xid, u_int16_t proto, u_int32_t ip, + u_int16_t port) +{ + struct request_p *req_p; + + /* Verifies if entry already exists */ + WRITE_LOCK(&ipct_rpc_udp_lock); + req_p = LIST_FIND(&request_p_list_udp, request_p_cmp, + struct request_p *, xid, ip, port); + + if (req_p) { + /* Refresh timeout */ + if (del_timer(&req_p->timeout)) { + req_p->timeout.expires = jiffies + EXP; + add_timer(&req_p->timeout); + } + WRITE_UNLOCK(&ipct_rpc_udp_lock); + return; + + } + WRITE_UNLOCK(&ipct_rpc_udp_lock); + + /* Allocate new request_p */ + req_p = (struct request_p *) kmalloc(sizeof(struct request_p), GFP_ATOMIC); + if (!req_p) { + DEBUGP("can't allocate request_p\n"); + return; + } + *req_p = ((struct request_p) {{ NULL, NULL }, xid, ip, port, proto, + { { NULL, NULL }, jiffies + EXP, (unsigned long)req_p, + NULL }}); + + /* Initialize timer */ + init_timer(&req_p->timeout); + req_p->timeout.function = delete_request_p; + add_timer(&req_p->timeout); + + /* Put in list */ + WRITE_LOCK(&ipct_rpc_udp_lock); + list_prepend(&request_p_list_udp, req_p); + WRITE_UNLOCK(&ipct_rpc_udp_lock); + return; + +} + + +static int check_rpc_packet(const u_int32_t *data, + int dir, struct ip_conntrack *ct, + struct list_head request_p_list) +{ + struct request_p *req_p; + u_int32_t xid; + struct ip_conntrack_expect expect, *exp = &expect; + + /* Translstion's buffer for XDR */ + u_int16_t port_buf; + + + /* Get XID */ + xid = *data; + + /* This does sanity checking on RPC payloads, + * and permits only the RPC "get port" (3) + * in authorised procedures in client + * communications with the portmapper. + */ + + /* perform direction dependant RPC work */ + if (dir == IP_CT_DIR_ORIGINAL) { + + data += 5; + + /* Get RPC requestor */ + if (IXDR_GET_INT32(data) != 3) { + DEBUGP("RPC packet contains an invalid (non \"get\") requestor. [skip]\n"); + return NF_ACCEPT; + } + DEBUGP("RPC packet contains a \"get\" requestor. [cont]\n"); + + data++; + + /* Jump Credentials and Verfifier */ + data = data + IXDR_GET_INT32(data) + 2; + data = data + IXDR_GET_INT32(data) + 2; + + /* Get RPC procedure */ + DEBUGP("RPC packet contains procedure request [%u]. [cont]\n", + (unsigned int)IXDR_GET_INT32(data)); + + /* Get RPC protocol and store against client parameters */ + data = data + 2; + alloc_request_p(xid, IXDR_GET_INT32(data), ct->tuplehash[dir].tuple.src.ip, + ct->tuplehash[dir].tuple.src.u.all); + + DEBUGP("allocated RPC req_p for xid=%u proto=%u %u.%u.%u.%u:%u\n", + xid, IXDR_GET_INT32(data), + NIPQUAD(ct->tuplehash[dir].tuple.src.ip), + ntohs(ct->tuplehash[dir].tuple.src.u.all)); + + DEBUGP("allocated RPC request for protocol %u. [done]\n", + (unsigned int)IXDR_GET_INT32(data)); + + } else { + + /* Check for returning packet's stored counterpart */ + req_p = LIST_FIND(&request_p_list_udp, request_p_cmp, + struct request_p *, xid, + ct->tuplehash[!dir].tuple.src.ip, + ct->tuplehash[!dir].tuple.src.u.all); + + /* Drop unexpected packets */ + if (!req_p) { + DEBUGP("packet is not expected. [skip]\n"); + return NF_ACCEPT; + } + + /* Verifies if packet is really an RPC reply packet */ + data = data++; + if (IXDR_GET_INT32(data) != 1) { + DEBUGP("packet is not a valid RPC reply. [skip]\n"); + return NF_ACCEPT; + } + + /* Is status accept? */ + data++; + if (IXDR_GET_INT32(data)) { + DEBUGP("packet is not an RPC accept. [skip]\n"); + return NF_ACCEPT; + } + + /* Get Verifier length. Jump verifier */ + data++; + data = data + IXDR_GET_INT32(data) + 2; + + /* Is accpet status "success"? */ + if (IXDR_GET_INT32(data)) { + DEBUGP("packet is not an RPC accept status of success. [skip]\n"); + return NF_ACCEPT; + } + + /* Get server port number */ + data++; + port_buf = (u_int16_t) IXDR_GET_INT32(data); + + /* If a packet has made it this far then it deserves an + * expectation ... if port == 0, then this service is + * not going to be registered. + */ + if (port_buf) { + DEBUGP("port found: %u\n", port_buf); + + memset(&expect, 0, sizeof(expect)); + + /* Watch out, Radioactive-Man! */ + exp->tuple.src.ip = ct->tuplehash[!dir].tuple.src.ip; + exp->tuple.dst.ip = ct->tuplehash[!dir].tuple.dst.ip; + exp->mask.src.ip = 0xffffffff; + exp->mask.dst.ip = 0xffffffff; + + switch (req_p->proto) { + case IPPROTO_UDP: + exp->tuple.src.u.udp.port = 0; + exp->tuple.dst.u.udp.port = htons(port_buf); + exp->tuple.dst.protonum = IPPROTO_UDP; + exp->mask.src.u.udp.port = 0; + exp->mask.dst.u.udp.port = htons(0xffff); + exp->mask.dst.protonum = 0xffff; + break; + + case IPPROTO_TCP: + exp->tuple.src.u.tcp.port = 0; + exp->tuple.dst.u.tcp.port = htons(port_buf); + exp->tuple.dst.protonum = IPPROTO_TCP; + exp->mask.src.u.tcp.port = 0; + exp->mask.dst.u.tcp.port = htons(0xffff); + exp->mask.dst.protonum = 0xffff; + break; + } + exp->expectfn = NULL; + + ip_conntrack_expect_related(ct, &expect); + + DEBUGP("expect related ip %u.%u.%u.%u:0-%u.%u.%u.%u:%u proto=%u\n", + NIPQUAD(exp->tuple.src.ip), + NIPQUAD(exp->tuple.dst.ip), + port_buf, req_p->proto); + + DEBUGP("expect related mask %u.%u.%u.%u:0-%u.%u.%u.%u:65535 proto=%u\n", + NIPQUAD(exp->mask.src.ip), + NIPQUAD(exp->mask.dst.ip), + exp->mask.dst.protonum); + + } + + req_cl(req_p); + + DEBUGP("packet evaluated. [expect]\n"); + return NF_ACCEPT; + } + + return NF_ACCEPT; + +} + + +/* RPC UDP helper */ +static int help(const struct iphdr *iph, size_t len, + struct ip_conntrack *ct, enum ip_conntrack_info ctinfo) +{ + struct udphdr *udph = (void *) iph + iph->ihl * 4; + const u_int32_t *data = (const u_int32_t *)udph + 2; + size_t udplen = len - iph->ihl * 4; + int dir = CTINFO2DIR(ctinfo); + int crp_ret; + + /* Checksum */ + const u_int16_t *chsm = (const u_int16_t *)udph + 3; + + + DEBUGP("new packet to evaluate ..\n"); + + /* Not whole UDP header? */ + if (udplen < sizeof(struct udphdr)) { + DEBUGP("UDP header length is; udplen=%u ..\n", (unsigned) udplen); + DEBUGP("packet does not contain a complete UDP header. [skip]\n"); + return NF_ACCEPT; + } + + /* FIXME: Source route IP option packets --RR */ + if (*chsm) { + if (csum_tcpudp_magic(iph->saddr, iph->daddr, udplen, IPPROTO_UDP, + csum_partial((char *)udph, udplen, 0))) { + DEBUGP("[note: failure to get past this error may indicate source routing]\n"); + DEBUGP("packet contains a bad checksum. [skip]\n"); + return NF_ACCEPT; + } + } + + /* perform direction dependant protocol work */ + if (dir == IP_CT_DIR_ORIGINAL) { + + DEBUGP("packet is from the initiator. [cont]\n"); + + /* Tests if packet len is ok */ + if ((udplen - sizeof(struct udphdr)) != 56) { + DEBUGP("packet length is not correct. [skip]\n"); + return NF_ACCEPT; + } + + } else { + + DEBUGP("packet is from the receiver. [cont]\n"); + + /* Until there's been traffic both ways, don't look in packets. */ + if (ctinfo != IP_CT_ESTABLISHED + IP_CT_IS_REPLY) { + DEBUGP("connection tracking state is; ctinfo=%u ..\n", ctinfo); + DEBUGP("[note: failure to get past this error may indicate asymmetric routing]\n"); + DEBUGP("packet is not yet part of a two way stream. [skip]\n"); + return NF_ACCEPT; + } + + /* Tests if packet len is ok */ + if ((udplen - sizeof(struct udphdr)) != 28) { + DEBUGP("packet length is not correct. [skip]\n"); + return NF_ACCEPT; + } + + } + + /* Get to the data */ + /* udp *data == *correct */ + + /* Check the RPC data */ + crp_ret = check_rpc_packet(data, dir, ct, request_p_list_udp); + + return crp_ret; + +} + + +static struct ip_conntrack_helper rpc_helpers[MAX_PORTS]; + +static void fini(void); + + +static int __init init(void) +{ + int port, ret; + static char name[10]; + + + /* If no port given, default to standard RPC port */ + if (ports[0] == 0) + ports[0] = RPC_PORT; + + for (port = 0; (port < MAX_PORTS) && ports[port]; port++) { + memset(&rpc_helpers[port], 0, sizeof(struct ip_conntrack_helper)); + + if (ports[port] == RPC_PORT) + sprintf(name, "rpc"); + else + sprintf(name, "rpc-%d", port); + + rpc_helpers[port].name = name; + rpc_helpers[port].me = THIS_MODULE; + rpc_helpers[port].max_expected = 1; + rpc_helpers[port].flags = IP_CT_HELPER_F_REUSE_EXPECT; + rpc_helpers[port].timeout = 0; + + rpc_helpers[port].tuple.dst.protonum = IPPROTO_UDP; + rpc_helpers[port].mask.dst.protonum = 0xffff; + + /* RPC can come from ports 0:65535 to ports[port] (111) */ + rpc_helpers[port].tuple.src.u.udp.port = htons(ports[port]); + rpc_helpers[port].mask.src.u.udp.port = htons(0xffff); + rpc_helpers[port].mask.dst.u.udp.port = htons(0x0); + + rpc_helpers[port].help = help; + + DEBUGP("registering helper for port #%d: %d/UDP\n", port, ports[port]); + DEBUGP("helper match ip %u.%u.%u.%u:%u->%u.%u.%u.%u:%u\n", + NIPQUAD(rpc_helpers[port].tuple.dst.ip), + ntohs(rpc_helpers[port].tuple.dst.u.udp.port), + NIPQUAD(rpc_helpers[port].tuple.src.ip), + ntohs(rpc_helpers[port].tuple.src.u.udp.port)); + DEBUGP("helper match mask %u.%u.%u.%u:%u->%u.%u.%u.%u:%u\n", + NIPQUAD(rpc_helpers[port].mask.dst.ip), + ntohs(rpc_helpers[port].mask.dst.u.udp.port), + NIPQUAD(rpc_helpers[port].mask.src.ip), + ntohs(rpc_helpers[port].mask.src.u.udp.port)); + + ret = ip_conntrack_helper_register(&rpc_helpers[port]); + + if (ret) { + printk("ERROR registering port %d\n", + ports[port]); + fini(); + return -EBUSY; + } + ports_n_c++; + } + return 0; +} + + +/* This function is intentionally _NOT_ defined as __exit, because + * it is needed by the init function */ +static void fini(void) +{ + int port; + + DEBUGP("cleaning request list\n"); + clean_request(&request_p_list_udp); + + for (port = 0; (port < ports_n_c) && ports[port]; port++) { + DEBUGP("unregistering port %d\n", ports[port]); + ip_conntrack_helper_unregister(&rpc_helpers[port]); + } +} + + +module_init(init); +module_exit(fini); + +struct module *ip_conntrack_rpc_udp = THIS_MODULE; +EXPORT_SYMBOL(request_p_list_udp); +EXPORT_SYMBOL(ip_conntrack_rpc_udp); +EXPORT_SYMBOL(ipct_rpc_udp_lock); + diff -Nur --exclude '*.orig' linux-2.6.6-rc1.org/net/ipv4/netfilter/ip_conntrack_rsh.c linux-2.6.6-rc1/net/ipv4/netfilter/ip_conntrack_rsh.c --- linux-2.6.6-rc1.org/net/ipv4/netfilter/ip_conntrack_rsh.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.6-rc1/net/ipv4/netfilter/ip_conntrack_rsh.c 2004-04-16 09:18:45.000000000 +0200 @@ -0,0 +1,331 @@ +/* RSH extension for IP connection tracking, Version 1.0 + * (C) 2002 by Ian (Larry) Latter + * based on HW's ip_conntrack_irc.c + * + * ip_conntrack_rsh.c,v 1.0 2002/07/17 14:49:26 + * + * 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. + ** + * Module load syntax: + * insmod ip_conntrack_rsh.o ports=port1,port2,...port + * + * please give the ports of all RSH servers You wish to connect to. + * If You don't specify ports, the default will be port 514 + ** + * Note to all: + * RSH blows ... you should use SSH (openssh.org) to replace it, + * unfortunately I babysit some sysadmins that won't migrate + * their legacy crap, in our second tier. + */ + + +/* + * Some docco ripped from the net to teach me all there is to know about + * RSH, in 16.5 seconds (ie, all of the non-netfilter docco used to write + * this module). + * + * I have no idea what "unix rshd man pages" these guys have .. but that + * is some pretty detailed docco! + ** + * + * 4. Of the rsh protocol. + * ----------------------- + * + * The rshd listens on TCP port #514. The following info is from the unix + * rshd man pages : + * + * "Service Request Protocol + * + * When the rshd daemon receives a service request, it initiates the + * following protocol: + * + * 1. The rshd daemon checks the source port number for the request. + * If the port number is not in the range 0 through 1023, the rshd daemon + * terminates the connection. + * + * 2. The rshd daemon reads characters from the socket up to a null byte. + * The string read is interpreted as an ASCII number (base 10). If this + * number is nonzero, the rshd daemon interprets it as the port number + * of a secondary stream to be used as standard error. A second connection + * is created to the specified port on the client host. The source port + * on the local host is in the range 0 through 1023. + * + * 3. The rshd daemon uses the source address of the initial connection + * request to determine the name of the client host. If the name cannot + * be determined, the rshd daemon uses the dotted decimal representation + * of the client host's address. + * + * 4. The rshd daemon retrieves the following information from the initial + * socket: + * + * * A null-terminated string of at most 16 bytes interpreted as + * the user name of the user on the client host. + * + * * A null-terminated string of at most 16 bytes interpreted as + * the user name to be used on the local server host. + * + * * Another null-terminated string interpreted as a command line + * to be passed to a shell on the local server host. + * + * 5. The rshd daemon attempts to validate the user using the following steps: + * + * a. The rshd daemon looks up the local user name in the /etc/passwd + * file and tries to switch to the home directory (using the chdir + * subroutine). If either the lookup or the directory change fails, + * the rshd daemon terminates the connection. + * + * b. If the local user ID is a nonzero value, the rshd daemon searches + * the /etc/hosts.equiv file to see if the name of the client + * workstation is listed. If the client workstation is listed as an + * equivalent host, the rshd daemon validates the user. + * + * c. If the $HOME/.rhosts file exists, the rshd daemon tries to + * authenticate the user by checking the .rhosts file. + * + * d. If either the $HOME/.rhosts authentication fails or the + * client host is not an equivalent host, the rshd daemon + * terminates the connection. + * + * 6. Once rshd validates the user, the rshd daemon returns a null byte + * on the initial connection and passes the command line to the user's + * local login shell. The shell then inherits the network connections + * established by the rshd daemon." + * + */ + + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#define MAX_PORTS 8 +static int ports[MAX_PORTS]; +static int ports_n_c = 0; + +MODULE_AUTHOR("Ian (Larry) Latter "); +MODULE_DESCRIPTION("RSH connection tracking module"); +MODULE_LICENSE("GPL"); +#ifdef MODULE_PARM +MODULE_PARM(ports, "1-" __MODULE_STRING(MAX_PORTS) "i"); +MODULE_PARM_DESC(ports, "port numbers of RSH servers"); +#endif + +DECLARE_LOCK(ip_rsh_lock); +struct module *ip_conntrack_rsh = THIS_MODULE; + +#if 0 +#define DEBUGP(format, args...) printk(KERN_DEBUG "ip_conntrack_rsh: " \ + format, ## args) +#else +#define DEBUGP(format, args...) +#endif + + + +/* FIXME: This should be in userspace. Later. */ +static int help(const struct iphdr *iph, size_t len, + struct ip_conntrack *ct, enum ip_conntrack_info ctinfo) +{ + /* tcplen not negative guarenteed by ip_conntrack_tcp.c */ + struct tcphdr *tcph = (void *) iph + iph->ihl * 4; + const char *data = (const char *) tcph + tcph->doff * 4; + u_int32_t tcplen = len - iph->ihl * 4; + int dir = CTINFO2DIR(ctinfo); + struct ip_conntrack_expect expect, *exp = &expect; + struct ip_ct_rsh_expect *exp_rsh_info = &exp->help.exp_rsh_info; + u_int16_t port; + int maxoctet; + + /* note that "maxoctet" is used to maintain sanity (8 was the + * original array size used in rshd/glibc) -- is there a + * vulnerability in rshd.c in the looped port *= 10? + */ + + + DEBUGP("entered\n"); + + /* bail if packet is not from RSH client */ + if (dir == IP_CT_DIR_REPLY) + return NF_ACCEPT; + + /* Until there's been traffic both ways, don't look in packets. */ + if (ctinfo != IP_CT_ESTABLISHED + && ctinfo != IP_CT_ESTABLISHED + IP_CT_IS_REPLY) { + DEBUGP("Conntrackinfo = %u\n", ctinfo); + return NF_ACCEPT; + } + + /* Not whole TCP header? */ + if (tcplen < sizeof(struct tcphdr) || tcplen < tcph->doff * 4) { + DEBUGP("tcplen = %u\n", (unsigned) tcplen); + return NF_ACCEPT; + } + + /* Checksum invalid? Ignore. */ + /* FIXME: Source route IP option packets --RR */ + if (tcp_v4_check(tcph, tcplen, iph->saddr, iph->daddr, + csum_partial((char *) tcph, tcplen, 0))) { + DEBUGP("bad csum: %p %u %u.%u.%u.%u %u.%u.%u.%u\n", + tcph, tcplen, NIPQUAD(iph->saddr), + NIPQUAD(iph->daddr)); + return NF_ACCEPT; + } + + /* find the rsh stderr port */ + maxoctet = 4; + port = 0; + for ( ; *data != 0 && maxoctet != 0; data++, maxoctet--) { + if (*data < 0) + return(1); + if (*data == 0) + break; + if (*data < 48 || *data > 57) { + DEBUGP("these aren't the packets you're looking for ..\n"); + return NF_ACCEPT; + } + port = port * 10 + ( *data - 48 ); + } + + /* dont relate sessions that try to expose the client */ + DEBUGP("found port %u\n", port); + if (port > 1023) { + DEBUGP("skipping, expected port size is greater than 1023!\n"); + return NF_ACCEPT; + } + + + LOCK_BH(&ip_rsh_lock); + + /* new(,related) connection is; + * reply + dst (uint)port + src port (0:1023) + */ + memset(&expect, 0, sizeof(expect)); + + /* save some discovered data, in case someone ever wants to write + * a NAT module for this bastard .. + */ + exp_rsh_info->port = port; + + DEBUGP("wrote info port=%u\n", exp_rsh_info->port); + + + /* Watch out, Radioactive-Man! */ + exp->tuple.src.ip = ct->tuplehash[!dir].tuple.src.ip; + exp->tuple.dst.ip = ct->tuplehash[!dir].tuple.dst.ip; + exp->tuple.src.u.tcp.port = 0; + exp->tuple.dst.u.tcp.port = htons(exp_rsh_info->port); + exp->tuple.dst.protonum = IPPROTO_TCP; + + exp->mask.src.ip = 0xffffffff; + exp->mask.dst.ip = 0xffffffff; + + exp->mask.src.u.tcp.port = htons(0xfc00); + exp->mask.dst.u.tcp.port = htons(0xfc00); + exp->mask.dst.protonum = 0xffff; + + exp->expectfn = NULL; + + ip_conntrack_expect_related(ct, &expect); + + DEBUGP("expect related ip %u.%u.%u.%u:%u-%u.%u.%u.%u:%u\n", + NIPQUAD(exp->tuple.src.ip), + ntohs(exp->tuple.src.u.tcp.port), + NIPQUAD(exp->tuple.dst.ip), + ntohs(exp->tuple.dst.u.tcp.port)); + + DEBUGP("expect related mask %u.%u.%u.%u:%u-%u.%u.%u.%u:%u\n", + NIPQUAD(exp->mask.src.ip), + ntohs(exp->mask.src.u.tcp.port), + NIPQUAD(exp->mask.dst.ip), + ntohs(exp->mask.dst.u.tcp.port)); + UNLOCK_BH(&ip_rsh_lock); + + return NF_ACCEPT; +} + +static struct ip_conntrack_helper rsh_helpers[MAX_PORTS]; + +static void fini(void); + +static int __init init(void) +{ + int port, ret; + static char name[10]; + + + /* If no port given, default to standard RSH port */ + if (ports[0] == 0) + ports[0] = RSH_PORT; + + for (port = 0; (port < MAX_PORTS) && ports[port]; port++) { + memset(&rsh_helpers[port], 0, sizeof(struct ip_conntrack_helper)); + + if (ports[port] == RSH_PORT) + sprintf(name, "rsh"); + else + sprintf(name, "rsh-%d", port); + + rsh_helpers[port].name = name; + rsh_helpers[port].me = THIS_MODULE; + rsh_helpers[port].max_expected = 1; + rsh_helpers[port].flags = IP_CT_HELPER_F_REUSE_EXPECT; + rsh_helpers[port].timeout = 0; + + rsh_helpers[port].tuple.dst.protonum = IPPROTO_TCP; + rsh_helpers[port].mask.dst.protonum = 0xffff; + + /* RSH must come from ports 0:1023 to ports[port] (514) */ + rsh_helpers[port].tuple.src.u.tcp.port = htons(ports[port]); + rsh_helpers[port].mask.src.u.tcp.port = htons(0xfc00); + rsh_helpers[port].mask.dst.u.tcp.port = htons(0xfc00); + + rsh_helpers[port].help = help; + + DEBUGP("registering helper for port #%d: %d/TCP\n", port, ports[port]); + DEBUGP("helper match ip %u.%u.%u.%u:%u-%u.%u.%u.%u:%u\n", + NIPQUAD(rsh_helpers[port].tuple.src.ip), + ntohs(rsh_helpers[port].tuple.src.u.tcp.port), + NIPQUAD(rsh_helpers[port].tuple.dst.ip), + ntohs(rsh_helpers[port].tuple.dst.u.tcp.port)); + DEBUGP("helper match mask %u.%u.%u.%u:%u-%u.%u.%u.%u:%u\n", + NIPQUAD(rsh_helpers[port].mask.src.ip), + ntohs(rsh_helpers[port].mask.src.u.tcp.port), + NIPQUAD(rsh_helpers[port].mask.dst.ip), + ntohs(rsh_helpers[port].mask.dst.u.tcp.port)); + + ret = ip_conntrack_helper_register(&rsh_helpers[port]); + + if (ret) { + printk("ERROR registering port %d\n", + ports[port]); + fini(); + return -EBUSY; + } + ports_n_c++; + } + return 0; +} + +/* This function is intentionally _NOT_ defined as __exit, because + * it is needed by the init function */ +static void fini(void) +{ + int port; + for (port = 0; (port < MAX_PORTS) && ports[port]; port++) { + DEBUGP("unregistering port %d\n", ports[port]); + ip_conntrack_helper_unregister(&rsh_helpers[port]); + } +} + +module_init(init); +module_exit(fini); 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-16 08:59:08.000000000 +0200 +++ linux-2.6.6-rc1/net/ipv4/netfilter/ip_conntrack_standalone.c 2004-04-16 09:17:11.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; @@ -633,6 +636,7 @@ EXPORT_SYMBOL(ip_conntrack_alter_reply); EXPORT_SYMBOL(ip_conntrack_destroyed); EXPORT_SYMBOL(ip_conntrack_get); +EXPORT_SYMBOL(__ip_conntrack_confirm); EXPORT_SYMBOL(need_ip_conntrack); EXPORT_SYMBOL(ip_conntrack_helper_register); EXPORT_SYMBOL(ip_conntrack_helper_unregister); diff -Nur --exclude '*.orig' linux-2.6.6-rc1.org/net/ipv4/netfilter/ip_conntrack_talk.c linux-2.6.6-rc1/net/ipv4/netfilter/ip_conntrack_talk.c --- linux-2.6.6-rc1.org/net/ipv4/netfilter/ip_conntrack_talk.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.6-rc1/net/ipv4/netfilter/ip_conntrack_talk.c 2004-04-16 09:18:58.000000000 +0200 @@ -0,0 +1,360 @@ +/* + * talk extension for IP connection tracking. + * Jozsef Kadlecsik + * + * 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. + ** + * Module load syntax: + * insmod ip_nat_talk.o talk=[0|1] ntalk=[0|1] ntalk2=[01] + * + * talk=[0|1] disable|enable old talk support + * ntalk=[0|1] disable|enable ntalk support + * ntalk2=[0|1] disable|enable ntalk2 support + * + * The default is talk=1 ntalk=1 ntalk2=1 + * + * The helper does not support simultaneous talk requests. + ** + * + * ASCII art on talk protocols + * + * + * caller server callee server + * | \ / + * | \ / + * | \ / + * | / + * | / \ + * 2 | 1 / \ 3 + * caller client ----------- callee client + * 4 + * + * 1. caller client <-> callee server: LOOK_UP, then ANNOUNCE invitation + * ( 2. caller client <-> caller server: LEAVE_INVITE to server ) + * 3. callee client <-> caller server: LOOK_UP invitation + * 4. callee client <-> caller client: talk data channel + * + * [1]: M. Hunter, talk: a historical protocol for interactive communication + * draft-hunter-talk-00.txt + * [2]: D.B. Chapman, E.D. Zwicky: Building Internet Firewalls (O'Reilly) + */ +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +/* Default all talk protocols are supported */ +static int talk = 1; +static int ntalk = 1; +static int ntalk2 = 1; +MODULE_AUTHOR("Jozsef Kadlecsik "); +MODULE_DESCRIPTION("talk connection tracking module"); +MODULE_LICENSE("GPL"); +#ifdef MODULE_PARM +MODULE_PARM(talk, "i"); +MODULE_PARM_DESC(talk, "support (old) talk protocol"); +MODULE_PARM(ntalk, "i"); +MODULE_PARM_DESC(ntalk, "support ntalk protocol"); +MODULE_PARM(ntalk2, "i"); +MODULE_PARM_DESC(ntalk2, "support ntalk2 protocol"); +#endif + +DECLARE_LOCK(ip_talk_lock); +struct module *ip_conntrack_talk = THIS_MODULE; + +#if 0 +#define DEBUGP printk +#else +#define DEBUGP(format, args...) +#endif + +static int talk_expect(struct ip_conntrack *ct); +static int ntalk_expect(struct ip_conntrack *ct); + +static int (*talk_expectfn[2])(struct ip_conntrack *ct) = {talk_expect, ntalk_expect}; + +static int talk_help_response(const struct iphdr *iph, size_t len, + struct ip_conntrack *ct, + enum ip_conntrack_info ctinfo, + int talk_port, + u_char mode, + u_char type, + u_char answer, + struct talk_addr *addr) +{ + int dir = CTINFO2DIR(ctinfo); + struct ip_conntrack_expect expect, *exp = &expect; + struct ip_ct_talk_expect *exp_talk_info = &exp->help.exp_talk_info; + + DEBUGP("ip_ct_talk_help_response: %u.%u.%u.%u:%u, type %d answer %d\n", + NIPQUAD(addr->ta_addr), ntohs(addr->ta_port), + type, answer); + + if (!(answer == SUCCESS && type == mode)) + return NF_ACCEPT; + + memset(&expect, 0, sizeof(expect)); + + if (type == ANNOUNCE) { + + DEBUGP("ip_ct_talk_help_response: ANNOUNCE\n"); + + /* update the talk info */ + LOCK_BH(&ip_talk_lock); + exp_talk_info->port = htons(talk_port); + + /* expect callee client -> caller server message */ + exp->tuple = ((struct ip_conntrack_tuple) + { { ct->tuplehash[dir].tuple.src.ip, + { 0 } }, + { ct->tuplehash[dir].tuple.dst.ip, + { .tcp = { htons(talk_port) } }, + IPPROTO_UDP }}); + exp->mask = ((struct ip_conntrack_tuple) + { { 0xFFFFFFFF, { 0 } }, + { 0xFFFFFFFF, { .tcp = { 0xFFFF } }, 0xFFFF }}); + + exp->expectfn = talk_expectfn[talk_port - TALK_PORT]; + + DEBUGP("ip_ct_talk_help_response: callee client %u.%u.%u.%u:%u -> caller daemon %u.%u.%u.%u:%u!\n", + NIPQUAD(exp->tuple.src.ip), ntohs(exp->tuple.src.u.udp.port), + NIPQUAD(exp->tuple.dst.ip), ntohs(exp->tuple.dst.u.udp.port)); + + /* Ignore failure; should only happen with NAT */ + ip_conntrack_expect_related(ct, &expect); + UNLOCK_BH(&ip_talk_lock); + } + if (type == LOOK_UP) { + + DEBUGP("ip_ct_talk_help_response: LOOK_UP\n"); + + /* update the talk info */ + LOCK_BH(&ip_talk_lock); + exp_talk_info->port = addr->ta_port; + + /* expect callee client -> caller client connection */ + exp->tuple = ((struct ip_conntrack_tuple) + { { ct->tuplehash[!dir].tuple.src.ip, + { 0 } }, + { addr->ta_addr, + { addr->ta_port }, + IPPROTO_TCP }}); + exp->mask = ((struct ip_conntrack_tuple) + { { 0xFFFFFFFF, { 0 } }, + { 0xFFFFFFFF, { 0xFFFF }, 0xFFFF }}); + + exp->expectfn = NULL; + + DEBUGP("ip_ct_talk_help_response: callee client %u.%u.%u.%u:%u -> caller client %u.%u.%u.%u:%u!\n", + NIPQUAD(exp->tuple.src.ip), ntohs(exp->tuple.src.u.tcp.port), + NIPQUAD(exp->tuple.dst.ip), ntohs(exp->tuple.dst.u.tcp.port)); + + /* Ignore failure; should only happen with NAT */ + ip_conntrack_expect_related(ct, &expect); + UNLOCK_BH(&ip_talk_lock); + } + + return NF_ACCEPT; +} + +/* FIXME: This should be in userspace. Later. */ +static int talk_help(const struct iphdr *iph, size_t len, + struct ip_conntrack *ct, + enum ip_conntrack_info ctinfo, + int talk_port, + u_char mode) +{ + struct udphdr *udph = (void *)iph + iph->ihl * 4; + const char *data = (const char *)udph + sizeof(struct udphdr); + int dir = CTINFO2DIR(ctinfo); + size_t udplen; + + DEBUGP("ip_ct_talk_help: help entered\n"); + + /* Until there's been traffic both ways, don't look in packets. */ + if (ctinfo != IP_CT_ESTABLISHED + && ctinfo != IP_CT_ESTABLISHED + IP_CT_IS_REPLY) { + DEBUGP("ip_ct_talk_help: Conntrackinfo = %u\n", ctinfo); + return NF_ACCEPT; + } + + /* Not whole UDP header? */ + udplen = len - iph->ihl * 4; + if (udplen < sizeof(struct udphdr)) { + DEBUGP("ip_ct_talk_help: too short for udph, udplen = %u\n", (unsigned)udplen); + return NF_ACCEPT; + } + + /* Checksum invalid? Ignore. */ + /* FIXME: Source route IP option packets --RR */ + if (csum_tcpudp_magic(iph->saddr, iph->daddr, udplen, IPPROTO_UDP, + csum_partial((char *)udph, udplen, 0))) { + DEBUGP("ip_ct_talk_help: bad csum: %p %u %u.%u.%u.%u %u.%u.%u.%u\n", + udph, udplen, NIPQUAD(iph->saddr), + NIPQUAD(iph->daddr)); + return NF_ACCEPT; + } + + DEBUGP("ip_ct_talk_help: %u.%u.%u.%u:%u->%u.%u.%u.%u:%u\n", + NIPQUAD(iph->saddr), ntohs(udph->source), NIPQUAD(iph->daddr), ntohs(udph->dest)); + + if (dir == IP_CT_DIR_ORIGINAL) + return NF_ACCEPT; + + if (talk_port == TALK_PORT + && udplen == sizeof(struct udphdr) + sizeof(struct talk_response)) + return talk_help_response(iph, len, ct, ctinfo, talk_port, mode, + ((struct talk_response *)data)->type, + ((struct talk_response *)data)->answer, + &(((struct talk_response *)data)->addr)); + else if (talk_port == NTALK_PORT + && ntalk + && udplen == sizeof(struct udphdr) + sizeof(struct ntalk_response) + && ((struct ntalk_response *)data)->vers == NTALK_VERSION) + return talk_help_response(iph, len, ct, ctinfo, talk_port, mode, + ((struct ntalk_response *)data)->type, + ((struct ntalk_response *)data)->answer, + &(((struct ntalk_response *)data)->addr)); + else if (talk_port == NTALK_PORT + && ntalk2 + && udplen >= sizeof(struct udphdr) + sizeof(struct ntalk2_response) + && ((struct ntalk2_response *)data)->vers == NTALK2_VERSION) + return talk_help_response(iph, len, ct, ctinfo, talk_port, mode, + ((struct ntalk2_response *)data)->type, + ((struct ntalk2_response *)data)->answer, + &(((struct ntalk2_response *)data)->addr)); + else { + DEBUGP("ip_ct_talk_help: not ntalk/ntalk2 response, datalen %u != %u or %u + max 256\n", + (unsigned)udplen - sizeof(struct udphdr), + sizeof(struct ntalk_response), sizeof(struct ntalk2_response)); + return NF_ACCEPT; + } +} + +static int lookup_help(const struct iphdr *iph, size_t len, + struct ip_conntrack *ct, enum ip_conntrack_info ctinfo) +{ + return talk_help(iph, len, ct, ctinfo, TALK_PORT, LOOK_UP); +} + +static int lookup_nhelp(const struct iphdr *iph, size_t len, + struct ip_conntrack *ct, enum ip_conntrack_info ctinfo) +{ + return talk_help(iph, len, ct, ctinfo, NTALK_PORT, LOOK_UP); +} + +static struct ip_conntrack_helper lookup_helpers[2] = + { { { NULL, NULL }, + "talk", /* name */ + 0, /* flags */ + NULL, /* module */ + 1, /* max_expected */ + 240, /* timeout */ + { { 0, { __constant_htons(TALK_PORT) } }, /* tuple */ + { 0, { 0 }, IPPROTO_UDP } }, + { { 0, { 0xFFFF } }, /* mask */ + { 0, { 0 }, 0xFFFF } }, + lookup_help }, /* helper */ + { { NULL, NULL }, + "ntalk", /* name */ + 0, /* flags */ + NULL, /* module */ + 1, /* max_expected */ + 240, /* timeout */ + { { 0, { __constant_htons(NTALK_PORT) } }, /* tuple */ + { 0, { 0 }, IPPROTO_UDP } }, + { { 0, { 0xFFFF } }, /* mask */ + { 0, { 0 }, 0xFFFF } }, + lookup_nhelp } /* helper */ + }; + +static int talk_expect(struct ip_conntrack *ct) +{ + DEBUGP("ip_conntrack_talk: calling talk_expectfn for ct %p\n", ct); + WRITE_LOCK(&ip_conntrack_lock); + ct->helper = &lookup_helpers[0]; + WRITE_UNLOCK(&ip_conntrack_lock); + + return NF_ACCEPT; /* unused */ +} + +static int ntalk_expect(struct ip_conntrack *ct) +{ + DEBUGP("ip_conntrack_talk: calling ntalk_expectfn for ct %p\n", ct); + WRITE_LOCK(&ip_conntrack_lock); + ct->helper = &lookup_helpers[1]; + WRITE_UNLOCK(&ip_conntrack_lock); + + return NF_ACCEPT; /* unused */ +} + +static int help(const struct iphdr *iph, size_t len, + struct ip_conntrack *ct, enum ip_conntrack_info ctinfo) +{ + return talk_help(iph, len, ct, ctinfo, TALK_PORT, ANNOUNCE); +} + +static int nhelp(const struct iphdr *iph, size_t len, + struct ip_conntrack *ct, enum ip_conntrack_info ctinfo) +{ + return talk_help(iph, len, ct, ctinfo, NTALK_PORT, ANNOUNCE); +} + +static struct ip_conntrack_helper talk_helpers[2] = + { { { NULL, NULL }, + "talk", /* name */ + 0, /* flags */ + THIS_MODULE, /* module */ + 1, /* max_expected */ + 240, /* timeout */ + { { 0, { __constant_htons(TALK_PORT) } }, /* tuple */ + { 0, { 0 }, IPPROTO_UDP } }, + { { 0, { 0xFFFF } }, /* mask */ + { 0, { 0 }, 0xFFFF } }, + help }, /* helper */ + { { NULL, NULL }, + "ntalk", /* name */ + 0, /* flags */ + THIS_MODULE, /* module */ + 1, /* max_expected */ + 240, /* timeout */ + { { 0, { __constant_htons(NTALK_PORT) } }, /* tuple */ + { 0, { 0 }, IPPROTO_UDP } }, + { { 0, { 0xFFFF } }, /* mask */ + { 0, { 0 }, 0xFFFF } }, + nhelp } /* helper */ + }; + +static int __init init(void) +{ + if (talk > 0) + ip_conntrack_helper_register(&talk_helpers[0]); + if (ntalk > 0 || ntalk2 > 0) + ip_conntrack_helper_register(&talk_helpers[1]); + + return 0; +} + +static void __exit fini(void) +{ + if (talk > 0) + ip_conntrack_helper_unregister(&talk_helpers[0]); + if (ntalk > 0 || ntalk2 > 0) + ip_conntrack_helper_unregister(&talk_helpers[1]); +} + +EXPORT_SYMBOL(ip_talk_lock); + +module_init(init); +module_exit(fini); diff -Nur --exclude '*.orig' linux-2.6.6-rc1.org/net/ipv4/netfilter/ip_nat_h323.c linux-2.6.6-rc1/net/ipv4/netfilter/ip_nat_h323.c --- linux-2.6.6-rc1.org/net/ipv4/netfilter/ip_nat_h323.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.6-rc1/net/ipv4/netfilter/ip_nat_h323.c 2004-04-16 09:17:05.000000000 +0200 @@ -0,0 +1,419 @@ +/* + * H.323 'brute force' extension for NAT alteration. + * Jozsef Kadlecsik + * + * Based on ip_masq_h323.c for 2.2 kernels from CoRiTel, Sofia project. + * (http://www.coritel.it/projects/sofia/nat.html) + * Uses Sampsa Ranta's excellent idea on using expectfn to 'bind' + * the unregistered helpers to the conntrack entries. + */ + + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +MODULE_AUTHOR("Jozsef Kadlecsik "); +MODULE_DESCRIPTION("H.323 'brute force' connection tracking module"); +MODULE_LICENSE("GPL"); + +DECLARE_LOCK_EXTERN(ip_h323_lock); +struct module *ip_nat_h323 = THIS_MODULE; + +#if 0 +#define DEBUGP printk +#else +#define DEBUGP(format, args...) +#endif + +/* FIXME: Time out? --RR */ + +static unsigned int +h225_nat_expected(struct sk_buff **pskb, + unsigned int hooknum, + struct ip_conntrack *ct, + struct ip_nat_info *info); + +static unsigned int h225_nat_help(struct ip_conntrack *ct, + struct ip_conntrack_expect *exp, + struct ip_nat_info *info, + enum ip_conntrack_info ctinfo, + unsigned int hooknum, + struct sk_buff **pskb); + +static struct ip_nat_helper h245 = + { { NULL, NULL }, + "H.245", /* name */ + 0, /* flags */ + NULL, /* module */ + { { 0, { 0 } }, /* tuple */ + { 0, { 0 }, IPPROTO_TCP } }, + { { 0, { 0xFFFF } }, /* mask */ + { 0, { 0 }, 0xFFFF } }, + h225_nat_help, /* helper */ + h225_nat_expected /* expectfn */ + }; + +static unsigned int +h225_nat_expected(struct sk_buff **pskb, + unsigned int hooknum, + struct ip_conntrack *ct, + struct ip_nat_info *info) +{ + struct ip_nat_multi_range mr; + u_int32_t newdstip, newsrcip, newip; + u_int16_t port; + struct ip_ct_h225_expect *exp_info; + struct ip_ct_h225_master *master_info; + struct ip_conntrack *master = master_ct(ct); + unsigned int is_h225, ret; + + IP_NF_ASSERT(info); + IP_NF_ASSERT(master); + + IP_NF_ASSERT(!(info->initialized & (1<master->expectant->help.ct_h225_info; + exp_info = &ct->master->help.exp_h225_info; + + LOCK_BH(&ip_h323_lock); + + DEBUGP("master: "); + DUMP_TUPLE(&master->tuplehash[IP_CT_DIR_ORIGINAL].tuple); + DUMP_TUPLE(&master->tuplehash[IP_CT_DIR_REPLY].tuple); + DEBUGP("conntrack: "); + DUMP_TUPLE(&ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple); + if (exp_info->dir == IP_CT_DIR_ORIGINAL) { + /* Make connection go to the client. */ + newdstip = master->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.ip; + newsrcip = master->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.ip; + DEBUGP("h225_nat_expected: %u.%u.%u.%u->%u.%u.%u.%u (to client)\n", + NIPQUAD(newsrcip), NIPQUAD(newdstip)); + } else { + /* Make the connection go to the server */ + newdstip = master->tuplehash[IP_CT_DIR_REPLY].tuple.src.ip; + newsrcip = master->tuplehash[IP_CT_DIR_REPLY].tuple.dst.ip; + DEBUGP("h225_nat_expected: %u.%u.%u.%u->%u.%u.%u.%u (to server)\n", + NIPQUAD(newsrcip), NIPQUAD(newdstip)); + } + port = exp_info->port; + is_h225 = master_info->is_h225 == H225_PORT; + UNLOCK_BH(&ip_h323_lock); + + if (HOOK2MANIP(hooknum) == IP_NAT_MANIP_SRC) + newip = newsrcip; + else + newip = newdstip; + + DEBUGP("h225_nat_expected: IP to %u.%u.%u.%u\n", NIPQUAD(newip)); + + mr.rangesize = 1; + /* We don't want to manip the per-protocol, just the IPs... */ + mr.range[0].flags = IP_NAT_RANGE_MAP_IPS; + mr.range[0].min_ip = mr.range[0].max_ip = newip; + + /* ... unless we're doing a MANIP_DST, in which case, make + sure we map to the correct port */ + if (HOOK2MANIP(hooknum) == IP_NAT_MANIP_DST) { + mr.range[0].flags |= IP_NAT_RANGE_PROTO_SPECIFIED; + mr.range[0].min = mr.range[0].max + = ((union ip_conntrack_manip_proto) + { .tcp = { port } }); + } + + ret = ip_nat_setup_info(ct, &mr, hooknum); + + if (is_h225) { + DEBUGP("h225_nat_expected: H.225, setting NAT helper for %p\n", ct); + /* NAT expectfn called with ip_nat_lock write-locked */ + info->helper = &h245; + } + return ret; +} + +static int h323_signal_address_fixup(struct ip_conntrack *ct, + struct sk_buff **pskb, + enum ip_conntrack_info ctinfo) +{ + struct iphdr *iph = (*pskb)->nh.iph; + struct tcphdr *tcph = (void *)iph + iph->ihl*4; + char *data = (char *) tcph + tcph->doff * 4; + u_int32_t tcplen = (*pskb)->len - iph->ihl*4; + u_int32_t datalen = tcplen - tcph->doff*4; + struct ip_ct_h225_master *info = &ct->help.ct_h225_info; + u_int32_t newip; + u_int16_t port; + int i; + + MUST_BE_LOCKED(&ip_h323_lock); + + DEBUGP("h323_signal_address_fixup: %s %s\n", + between(info->seq[IP_CT_DIR_ORIGINAL], ntohl(tcph->seq), ntohl(tcph->seq) + datalen) + ? "yes" : "no", + between(info->seq[IP_CT_DIR_REPLY], ntohl(tcph->seq), ntohl(tcph->seq) + datalen) + ? "yes" : "no"); + if (!(between(info->seq[IP_CT_DIR_ORIGINAL], ntohl(tcph->seq), ntohl(tcph->seq) + datalen) + || between(info->seq[IP_CT_DIR_REPLY], ntohl(tcph->seq), ntohl(tcph->seq) + datalen))) + return 1; + + DEBUGP("h323_signal_address_fixup: offsets %u + 6 and %u + 6 in %u\n", + info->offset[IP_CT_DIR_ORIGINAL], + info->offset[IP_CT_DIR_REPLY], + tcplen); + DUMP_TUPLE(&ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple); + DUMP_TUPLE(&ct->tuplehash[IP_CT_DIR_REPLY].tuple); + + for (i = 0; i < IP_CT_DIR_MAX; i++) { + DEBUGP("h323_signal_address_fixup: %s %s\n", + info->dir == IP_CT_DIR_ORIGINAL ? "original" : "reply", + i == IP_CT_DIR_ORIGINAL ? "caller" : "callee"); + if (!between(info->seq[i], ntohl(tcph->seq), + ntohl(tcph->seq) + datalen)) + continue; + if (!between(info->seq[i] + 6, ntohl(tcph->seq), + ntohl(tcph->seq) + datalen)) { + /* Partial retransmisison. It's a cracker being funky. */ + if (net_ratelimit()) { + printk("H.323_NAT: partial packet %u/6 in %u/%u\n", + info->seq[i], + ntohl(tcph->seq), + ntohl(tcph->seq) + datalen); + } + return 0; + } + + /* Change address inside packet to match way we're mapping + this connection. */ + if (i == IP_CT_DIR_ORIGINAL) { + newip = ct->tuplehash[!info->dir].tuple.dst.ip; + port = ct->tuplehash[!info->dir].tuple.dst.u.tcp.port; + } else { + newip = ct->tuplehash[!info->dir].tuple.src.ip; + port = ct->tuplehash[!info->dir].tuple.src.u.tcp.port; + } + + DEBUGP("h323_signal_address_fixup: orig %s IP:port %u.%u.%u.%u:%u\n", + i == IP_CT_DIR_ORIGINAL ? "source" : "dest ", + NIPQUAD(*((u_int32_t *)(data + info->offset[i]))), + ntohs(*((u_int16_t *)(data + info->offset[i] + 4)))); + + /* Modify the packet */ + *(u_int32_t *)(data + info->offset[i]) = newip; + *(u_int16_t *)(data + info->offset[i] + 4) = port; + + DEBUGP("h323_signal_address_fixup: new %s IP:port %u.%u.%u.%u:%u\n", + i == IP_CT_DIR_ORIGINAL ? "source" : "dest ", + NIPQUAD(*((u_int32_t *)(data + info->offset[i]))), + ntohs(*((u_int16_t *)(data + info->offset[i] + 4)))); + } + + /* fix checksum information */ + + (*pskb)->csum = csum_partial((char *)tcph + tcph->doff*4, + datalen, 0); + + tcph->check = 0; + tcph->check = tcp_v4_check(tcph, tcplen, iph->saddr, iph->daddr, + csum_partial((char *)tcph, tcph->doff*4, + (*pskb)->csum)); + ip_send_check(iph); + + return 1; +} + +static int h323_data_fixup(struct ip_ct_h225_expect *info, + struct ip_conntrack *ct, + struct sk_buff **pskb, + enum ip_conntrack_info ctinfo, + struct ip_conntrack_expect *expect) +{ + u_int32_t newip; + u_int16_t port; + struct ip_conntrack_tuple newtuple; + struct iphdr *iph = (*pskb)->nh.iph; + struct tcphdr *tcph = (void *)iph + iph->ihl*4; + char *data = (char *) tcph + tcph->doff * 4; + u_int32_t tcplen = (*pskb)->len - iph->ihl*4; + struct ip_ct_h225_master *master_info = &ct->help.ct_h225_info; + int is_h225; + + MUST_BE_LOCKED(&ip_h323_lock); + DEBUGP("h323_data_fixup: offset %u + 6 in %u\n", info->offset, tcplen); + DUMP_TUPLE(&ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple); + DUMP_TUPLE(&ct->tuplehash[IP_CT_DIR_REPLY].tuple); + + if (!between(expect->seq + 6, ntohl(tcph->seq), + ntohl(tcph->seq) + tcplen - tcph->doff * 4)) { + /* Partial retransmisison. It's a cracker being funky. */ + if (net_ratelimit()) { + printk("H.323_NAT: partial packet %u/6 in %u/%u\n", + expect->seq, + ntohl(tcph->seq), + ntohl(tcph->seq) + tcplen - tcph->doff * 4); + } + return 0; + } + + /* Change address inside packet to match way we're mapping + this connection. */ + if (info->dir == IP_CT_DIR_REPLY) { + /* Must be where client thinks server is */ + newip = ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.ip; + /* Expect something from client->server */ + newtuple.src.ip = ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.ip; + newtuple.dst.ip = ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.ip; + } else { + /* Must be where server thinks client is */ + newip = ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.ip; + /* Expect something from server->client */ + newtuple.src.ip = ct->tuplehash[IP_CT_DIR_REPLY].tuple.src.ip; + newtuple.dst.ip = ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.ip; + } + + is_h225 = (master_info->is_h225 == H225_PORT); + + if (is_h225) { + newtuple.dst.protonum = IPPROTO_TCP; + newtuple.src.u.tcp.port = expect->tuple.src.u.tcp.port; + } else { + newtuple.dst.protonum = IPPROTO_UDP; + newtuple.src.u.udp.port = expect->tuple.src.u.udp.port; + } + + /* Try to get same port: if not, try to change it. */ + for (port = ntohs(info->port); port != 0; port++) { + if (is_h225) + newtuple.dst.u.tcp.port = htons(port); + else + newtuple.dst.u.udp.port = htons(port); + + if (ip_conntrack_change_expect(expect, &newtuple) == 0) + break; + } + if (port == 0) { + DEBUGP("h323_data_fixup: no free port found!\n"); + return 0; + } + + port = htons(port); + + DEBUGP("h323_data_fixup: orig IP:port %u.%u.%u.%u:%u\n", + NIPQUAD(*((u_int32_t *)(data + info->offset))), + ntohs(*((u_int16_t *)(data + info->offset + 4)))); + + /* Modify the packet */ + *(u_int32_t *)(data + info->offset) = newip; + *(u_int16_t *)(data + info->offset + 4) = port; + + DEBUGP("h323_data_fixup: new IP:port %u.%u.%u.%u:%u\n", + NIPQUAD(*((u_int32_t *)(data + info->offset))), + ntohs(*((u_int16_t *)(data + info->offset + 4)))); + + /* fix checksum information */ + /* FIXME: usually repeated multiple times in the case of H.245! */ + + (*pskb)->csum = csum_partial((char *)tcph + tcph->doff*4, + tcplen - tcph->doff*4, 0); + + tcph->check = 0; + tcph->check = tcp_v4_check(tcph, tcplen, iph->saddr, iph->daddr, + csum_partial((char *)tcph, tcph->doff*4, + (*pskb)->csum)); + ip_send_check(iph); + + return 1; +} + +static unsigned int h225_nat_help(struct ip_conntrack *ct, + struct ip_conntrack_expect *exp, + struct ip_nat_info *info, + enum ip_conntrack_info ctinfo, + unsigned int hooknum, + struct sk_buff **pskb) +{ + int dir; + struct ip_ct_h225_expect *exp_info; + + /* Only mangle things once: original direction in POST_ROUTING + and reply direction on PRE_ROUTING. */ + dir = CTINFO2DIR(ctinfo); + DEBUGP("nat_h323: dir %s at hook %s\n", + dir == IP_CT_DIR_ORIGINAL ? "ORIG" : "REPLY", + hooknum == NF_IP_POST_ROUTING ? "POSTROUTING" + : hooknum == NF_IP_PRE_ROUTING ? "PREROUTING" + : hooknum == NF_IP_LOCAL_OUT ? "OUTPUT" : "???"); + if (!((hooknum == NF_IP_POST_ROUTING && dir == IP_CT_DIR_ORIGINAL) + || (hooknum == NF_IP_PRE_ROUTING && dir == IP_CT_DIR_REPLY))) { + DEBUGP("nat_h323: Not touching dir %s at hook %s\n", + dir == IP_CT_DIR_ORIGINAL ? "ORIG" : "REPLY", + hooknum == NF_IP_POST_ROUTING ? "POSTROUTING" + : hooknum == NF_IP_PRE_ROUTING ? "PREROUTING" + : hooknum == NF_IP_LOCAL_OUT ? "OUTPUT" : "???"); + return NF_ACCEPT; + } + + if (!exp) { + LOCK_BH(&ip_h323_lock); + if (!h323_signal_address_fixup(ct, pskb, ctinfo)) { + UNLOCK_BH(&ip_h323_lock); + return NF_DROP; + } + UNLOCK_BH(&ip_h323_lock); + return NF_ACCEPT; + } + + exp_info = &exp->help.exp_h225_info; + + LOCK_BH(&ip_h323_lock); + if (!h323_data_fixup(exp_info, ct, pskb, ctinfo, exp)) { + UNLOCK_BH(&ip_h323_lock); + return NF_DROP; + } + UNLOCK_BH(&ip_h323_lock); + + return NF_ACCEPT; +} + +static struct ip_nat_helper h225 = + { { NULL, NULL }, + "H.225", /* name */ + IP_NAT_HELPER_F_ALWAYS, /* flags */ + THIS_MODULE, /* module */ + { { 0, { .tcp = { __constant_htons(H225_PORT) } } }, /* tuple */ + { 0, { 0 }, IPPROTO_TCP } }, + { { 0, { .tcp = { 0xFFFF } } }, /* mask */ + { 0, { 0 }, 0xFFFF } }, + h225_nat_help, /* helper */ + h225_nat_expected /* expectfn */ + }; + +static int __init init(void) +{ + int ret; + + ret = ip_nat_helper_register(&h225); + + if (ret != 0) + printk("ip_nat_h323: cannot initialize the module!\n"); + + return ret; +} + +static void __exit fini(void) +{ + ip_nat_helper_unregister(&h225); +} + +module_init(init); +module_exit(fini); diff -Nur --exclude '*.orig' linux-2.6.6-rc1.org/net/ipv4/netfilter/ip_nat_mms.c linux-2.6.6-rc1/net/ipv4/netfilter/ip_nat_mms.c --- linux-2.6.6-rc1.org/net/ipv4/netfilter/ip_nat_mms.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.6-rc1/net/ipv4/netfilter/ip_nat_mms.c 2004-04-16 09:17:32.000000000 +0200 @@ -0,0 +1,350 @@ +/* MMS extension for TCP NAT alteration. + * (C) 2002 by Filip Sneppe + * based on ip_nat_ftp.c and ip_nat_irc.c + * + * ip_nat_mms.c v0.3 2002-09-22 + * + * 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. + * + * Module load syntax: + * insmod ip_nat_mms.o ports=port1,port2,...port + * + * Please give the ports of all MMS servers You wish to connect to. + * If you don't specify ports, the default will be TCP port 1755. + * + * More info on MMS protocol, firewalls and NAT: + * http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnwmt/html/MMSFirewall.asp + * http://www.microsoft.com/windows/windowsmedia/serve/firewall.asp + * + * The SDP project people are reverse-engineering MMS: + * http://get.to/sdp + */ + +/* FIXME: issue with UDP & fragmentation with this URL: + http://www.cnn.com/video/world/2002/01/21/jb.shoe.bomb.cafe.cnn.low.asx + may be related to out-of-order first packets: + basically the expectation is set up correctly, then the server sends + a first UDP packet which is fragmented plus arrives out-of-order. + the MASQUERADING firewall with ip_nat_mms loaded responds with + an ICMP unreachable back to the server */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if 0 +#define DEBUGP printk +#define DUMP_BYTES(address, counter) \ +({ \ + int temp_counter; \ + for(temp_counter=0; temp_counter"); +MODULE_DESCRIPTION("Microsoft Windows Media Services (MMS) NAT module"); +MODULE_LICENSE("GPL"); + +DECLARE_LOCK_EXTERN(ip_mms_lock); + +/* FIXME: Time out? --RR */ + +static int mms_data_fixup(const struct ip_ct_mms_expect *ct_mms_info, + struct ip_conntrack *ct, + struct sk_buff **pskb, + enum ip_conntrack_info ctinfo, + struct ip_conntrack_expect *expect) +{ + u_int32_t newip; + struct ip_conntrack_tuple t; + struct iphdr *iph = (*pskb)->nh.iph; + struct tcphdr *tcph = (void *) iph + iph->ihl * 4; + char *data = (char *)tcph + tcph->doff * 4; + int i, j, k, port; + u_int16_t mms_proto; + + u_int32_t *mms_chunkLenLV = (u_int32_t *)(data + MMS_SRV_CHUNKLENLV_OFFSET); + u_int32_t *mms_chunkLenLM = (u_int32_t *)(data + MMS_SRV_CHUNKLENLM_OFFSET); + u_int32_t *mms_messageLength = (u_int32_t *)(data + MMS_SRV_MESSAGELENGTH_OFFSET); + + int zero_padding; + + char buffer[28]; /* "\\255.255.255.255\UDP\65635" * 2 (for unicode) */ + char unicode_buffer[75]; /* 27*2 (unicode) + 20 + 1 */ + char proto_string[6]; + + MUST_BE_LOCKED(&ip_mms_lock); + + /* what was the protocol again ? */ + mms_proto = expect->tuple.dst.protonum; + sprintf(proto_string, "%u", mms_proto); + + DEBUGP("ip_nat_mms: mms_data_fixup: info (seq %u + %u) in %u, proto %s\n", + expect->seq, ct_mms_info->len, ntohl(tcph->seq), + mms_proto == IPPROTO_UDP ? "UDP" + : mms_proto == IPPROTO_TCP ? "TCP":proto_string); + + newip = ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.ip; + + /* Alter conntrack's expectations. */ + t = expect->tuple; + t.dst.ip = newip; + for (port = ct_mms_info->port; port != 0; port++) { + t.dst.u.tcp.port = htons(port); + if (ip_conntrack_change_expect(expect, &t) == 0) { + DEBUGP("ip_nat_mms: mms_data_fixup: using port %d\n", port); + break; + } + } + + if(port == 0) + return 0; + + sprintf(buffer, "\\\\%u.%u.%u.%u\\%s\\%u", + NIPQUAD(newip), + expect->tuple.dst.protonum == IPPROTO_UDP ? "UDP" + : expect->tuple.dst.protonum == IPPROTO_TCP ? "TCP":proto_string, + port); + DEBUGP("ip_nat_mms: new unicode string=%s\n", buffer); + + memset(unicode_buffer, 0, sizeof(char)*75); + + for (i=0; ipadding, ct_mms_info->len); + DEBUGP("ip_nat_mms: mms_data_fixup: offset: %u\n", MMS_SRV_UNICODE_STRING_OFFSET+ct_mms_info->len); + DUMP_BYTES(data+MMS_SRV_UNICODE_STRING_OFFSET, 60); + + /* add end of packet to it */ + for (j=0; jpadding; ++j) { + DEBUGP("ip_nat_mms: mms_data_fixup: i=%u j=%u byte=%u\n", + i, j, (u8)*(data+MMS_SRV_UNICODE_STRING_OFFSET+ct_mms_info->len+j)); + *(unicode_buffer+i*2+j) = *(data+MMS_SRV_UNICODE_STRING_OFFSET+ct_mms_info->len+j); + } + + /* pad with zeroes at the end ? see explanation of weird math below */ + zero_padding = (8-(strlen(buffer)*2 + ct_mms_info->padding + 4)%8)%8; + for (k=0; k chunkLenLV=%u chunkLenLM=%u messageLength=%u\n", + *mms_chunkLenLV, *mms_chunkLenLM, *mms_messageLength); + + /* explanation, before I forget what I did: + strlen(buffer)*2 + ct_mms_info->padding + 4 must be divisable by 8; + divide by 8 and add 3 to compute the mms_chunkLenLM field, + but note that things may have to be padded with zeroes to align by 8 + bytes, hence we add 7 and divide by 8 to get the correct length */ + *mms_chunkLenLM = (u_int32_t) (3+(strlen(buffer)*2+ct_mms_info->padding+11)/8); + *mms_chunkLenLV = *mms_chunkLenLM+2; + *mms_messageLength = *mms_chunkLenLV*8; + + DEBUGP("ip_nat_mms: modified=> chunkLenLV=%u chunkLenLM=%u messageLength=%u\n", + *mms_chunkLenLV, *mms_chunkLenLM, *mms_messageLength); + + ip_nat_mangle_tcp_packet(pskb, ct, ctinfo, + expect->seq - ntohl(tcph->seq), + ct_mms_info->len + ct_mms_info->padding, unicode_buffer, + strlen(buffer)*2 + ct_mms_info->padding + zero_padding); + DUMP_BYTES(unicode_buffer, 60); + + return 1; +} + +static unsigned int +mms_nat_expected(struct sk_buff **pskb, + unsigned int hooknum, + struct ip_conntrack *ct, + struct ip_nat_info *info) +{ + struct ip_nat_multi_range mr; + u_int32_t newdstip, newsrcip, newip; + + struct ip_conntrack *master = master_ct(ct); + + IP_NF_ASSERT(info); + IP_NF_ASSERT(master); + + IP_NF_ASSERT(!(info->initialized & (1 << HOOK2MANIP(hooknum)))); + + DEBUGP("ip_nat_mms: mms_nat_expected: We have a connection!\n"); + + newdstip = master->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.ip; + newsrcip = ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.ip; + DEBUGP("ip_nat_mms: mms_nat_expected: hook %s: newsrc->newdst %u.%u.%u.%u->%u.%u.%u.%u\n", + hooknum == NF_IP_POST_ROUTING ? "POSTROUTING" + : hooknum == NF_IP_PRE_ROUTING ? "PREROUTING" + : hooknum == NF_IP_LOCAL_OUT ? "OUTPUT" : "???", + NIPQUAD(newsrcip), NIPQUAD(newdstip)); + + if (HOOK2MANIP(hooknum) == IP_NAT_MANIP_SRC) + newip = newsrcip; + else + newip = newdstip; + + DEBUGP("ip_nat_mms: mms_nat_expected: IP to %u.%u.%u.%u\n", NIPQUAD(newip)); + + mr.rangesize = 1; + /* We don't want to manip the per-protocol, just the IPs. */ + mr.range[0].flags = IP_NAT_RANGE_MAP_IPS; + mr.range[0].min_ip = mr.range[0].max_ip = newip; + + return ip_nat_setup_info(ct, &mr, hooknum); +} + + +static unsigned int mms_nat_help(struct ip_conntrack *ct, + struct ip_conntrack_expect *exp, + struct ip_nat_info *info, + enum ip_conntrack_info ctinfo, + unsigned int hooknum, + struct sk_buff **pskb) +{ + struct iphdr *iph = (*pskb)->nh.iph; + struct tcphdr *tcph = (void *) iph + iph->ihl * 4; + unsigned int datalen; + int dir; + struct ip_ct_mms_expect *ct_mms_info; + + if (!exp) + DEBUGP("ip_nat_mms: no exp!!"); + + ct_mms_info = &exp->help.exp_mms_info; + + /* Only mangle things once: original direction in POST_ROUTING + and reply direction on PRE_ROUTING. */ + dir = CTINFO2DIR(ctinfo); + if (!((hooknum == NF_IP_POST_ROUTING && dir == IP_CT_DIR_ORIGINAL) + ||(hooknum == NF_IP_PRE_ROUTING && dir == IP_CT_DIR_REPLY))) { + DEBUGP("ip_nat_mms: mms_nat_help: not touching dir %s at hook %s\n", + dir == IP_CT_DIR_ORIGINAL ? "ORIG" : "REPLY", + hooknum == NF_IP_POST_ROUTING ? "POSTROUTING" + : hooknum == NF_IP_PRE_ROUTING ? "PREROUTING" + : hooknum == NF_IP_LOCAL_OUT ? "OUTPUT" : "???"); + return NF_ACCEPT; + } + DEBUGP("ip_nat_mms: mms_nat_help: beyond not touching (dir %s at hook %s)\n", + dir == IP_CT_DIR_ORIGINAL ? "ORIG" : "REPLY", + hooknum == NF_IP_POST_ROUTING ? "POSTROUTING" + : hooknum == NF_IP_PRE_ROUTING ? "PREROUTING" + : hooknum == NF_IP_LOCAL_OUT ? "OUTPUT" : "???"); + + datalen = (*pskb)->len - iph->ihl * 4 - tcph->doff * 4; + + DEBUGP("ip_nat_mms: mms_nat_help: %u+%u=%u %u %u\n", exp->seq, ct_mms_info->len, + exp->seq + ct_mms_info->len, + ntohl(tcph->seq), + ntohl(tcph->seq) + datalen); + + LOCK_BH(&ip_mms_lock); + /* Check wether the whole IP/proto/port pattern is carried in the payload */ + if (between(exp->seq + ct_mms_info->len, + ntohl(tcph->seq), + ntohl(tcph->seq) + datalen)) { + if (!mms_data_fixup(ct_mms_info, ct, pskb, ctinfo, exp)) { + UNLOCK_BH(&ip_mms_lock); + return NF_DROP; + } + } else { + /* Half a match? This means a partial retransmisison. + It's a cracker being funky. */ + if (net_ratelimit()) { + printk("ip_nat_mms: partial packet %u/%u in %u/%u\n", + exp->seq, ct_mms_info->len, + ntohl(tcph->seq), + ntohl(tcph->seq) + datalen); + } + UNLOCK_BH(&ip_mms_lock); + return NF_DROP; + } + UNLOCK_BH(&ip_mms_lock); + + return NF_ACCEPT; +} + +static struct ip_nat_helper mms[MAX_PORTS]; +static char mms_names[MAX_PORTS][10]; + +/* Not __exit: called from init() */ +static void fini(void) +{ + int i; + + for (i = 0; (i < MAX_PORTS) && ports[i]; i++) { + DEBUGP("ip_nat_mms: unregistering helper for port %d\n", ports[i]); + ip_nat_helper_unregister(&mms[i]); + } +} + +static int __init init(void) +{ + int i, ret = 0; + char *tmpname; + + if (ports[0] == 0) + ports[0] = MMS_PORT; + + for (i = 0; (i < MAX_PORTS) && ports[i]; i++) { + + memset(&mms[i], 0, sizeof(struct ip_nat_helper)); + + mms[i].tuple.dst.protonum = IPPROTO_TCP; + mms[i].tuple.src.u.tcp.port = htons(ports[i]); + mms[i].mask.dst.protonum = 0xFFFF; + mms[i].mask.src.u.tcp.port = 0xFFFF; + mms[i].help = mms_nat_help; + mms[i].me = THIS_MODULE; + mms[i].flags = 0; + mms[i].expect = mms_nat_expected; + + tmpname = &mms_names[i][0]; + if (ports[i] == MMS_PORT) + sprintf(tmpname, "mms"); + else + sprintf(tmpname, "mms-%d", i); + mms[i].name = tmpname; + + DEBUGP("ip_nat_mms: register helper for port %d\n", + ports[i]); + ret = ip_nat_helper_register(&mms[i]); + + if (ret) { + printk("ip_nat_mms: error registering " + "helper for port %d\n", ports[i]); + fini(); + return ret; + } + ports_c++; + } + + return ret; +} + +module_init(init); +module_exit(fini); diff -Nur --exclude '*.orig' linux-2.6.6-rc1.org/net/ipv4/netfilter/ip_nat_quake3.c linux-2.6.6-rc1/net/ipv4/netfilter/ip_nat_quake3.c --- linux-2.6.6-rc1.org/net/ipv4/netfilter/ip_nat_quake3.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.6-rc1/net/ipv4/netfilter/ip_nat_quake3.c 2004-04-16 09:18:43.000000000 +0200 @@ -0,0 +1,249 @@ +/* Quake3 extension for UDP NAT alteration. + * (C) 2002 by Filip Sneppe + * based on ip_nat_ftp.c and ip_nat_tftp.c + * + * ip_nat_quake3.c v0.0.3 2002-08-31 + * + * 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. + * + * Module load syntax: + * insmod ip_nat_quake3.o ports=port1,port2,...port + * + * please give the ports of all Quake3 master servers You wish to + * connect to. If you don't specify ports, the default will be UDP + * port 27950. + * + * Thanks to the Ethereal folks for their analysis of the Quake3 protocol. + * + * Notes: + * - If you're one of those people who would try anything to lower + * latency while playing Quake (and who isn't :-) ), you may want to + * consider not loading ip_nat_quake3 at all and just MASQUERADE all + * outgoing UDP traffic. + * This will make ip_conntrack_quake3 add the necessary expectations, + * but there will be no overhead for client->server UDP streams. If + * ip_nat_quake3 is loaded, quake3_nat_expected will be called per NAT + * hook for every packet in the client->server UDP stream. + * - Only SNAT/MASQUEARDE targets are useful for ip_nat_quake3. + * The IP addresses in the master connection payload (=IP addresses + * of Quake servers) have no relation with the master server so + * DNAT'ing the master connection to a server should not change the + * expected connections. + * - Not tested due to lack of equipment: + * - multiple Quake3 clients behind one MASQUERADE gateway + * - what if Quake3 client is running on router too + */ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +MODULE_AUTHOR("Filip Sneppe "); +MODULE_DESCRIPTION("Netfilter NAT helper for Quake III Arena"); +MODULE_LICENSE("GPL"); + +#define MAX_PORTS 8 + +static int ports[MAX_PORTS]; +static int ports_c = 0; +#ifdef MODULE_PARM +MODULE_PARM(ports,"1-" __MODULE_STRING(MAX_PORTS) "i"); +MODULE_PARM_DESC(ports, "port numbers of Quake III master servers"); +#endif + +/* Quake3 master server reply will add > 100 expectations per reply packet; when + doing lots of printk's, klogd may not be able to read /proc/kmsg fast enough */ +#if 0 +#define DEBUGP printk +#else +#define DEBUGP(format, args...) +#endif + +static struct quake3_search quake3s_nat = { "****", "getserversResponse", sizeof("getserversResponse") - 1 }; + +static unsigned int +quake3_nat_help(struct ip_conntrack *ct, + struct ip_conntrack_expect *exp, + struct ip_nat_info *info, + enum ip_conntrack_info ctinfo, + unsigned int hooknum, + struct sk_buff **pskb) +{ + struct iphdr *iph = (*pskb)->nh.iph; + struct udphdr *udph = (void *)iph + iph->ihl * 4; + struct ip_conntrack_tuple repl; + int dir = CTINFO2DIR(ctinfo); + int i; + + DEBUGP("ip_nat_quake3: quake3_nat_help, direction: %s hook: %s\n", + dir == IP_CT_DIR_ORIGINAL ? "ORIG" : "REPLY", + hooknum == NF_IP_POST_ROUTING ? "POSTROUTING" + : hooknum == NF_IP_PRE_ROUTING ? "PREROUTING" + : hooknum == NF_IP_LOCAL_OUT ? "OUTPUT" : "???" + ); + DUMP_TUPLE(&ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple); + DUMP_TUPLE(&ct->tuplehash[IP_CT_DIR_REPLY].tuple); + + /* Only mangle things once: original direction in POST_ROUTING + and reply direction on PRE_ROUTING. */ + if (!((hooknum == NF_IP_POST_ROUTING && dir == IP_CT_DIR_ORIGINAL) + || (hooknum == NF_IP_PRE_ROUTING && dir == IP_CT_DIR_REPLY))) { + DEBUGP("ip_nat_quake3: Not touching dir %s at hook %s\n", + dir == IP_CT_DIR_ORIGINAL ? "ORIG" : "REPLY", + hooknum == NF_IP_POST_ROUTING ? "POSTROUTING" + : hooknum == NF_IP_PRE_ROUTING ? "PREROUTING" + : hooknum == NF_IP_LOCAL_OUT ? "OUTPUT" : "????"); + return NF_ACCEPT; + } + + if (!exp) { + DEBUGP("no conntrack expectation to modify\n"); + return NF_ACCEPT; + } + + if (strnicmp((const char *)udph + 12, quake3s_nat.pattern, quake3s_nat.plen) == 0) { + for(i=31; /* 8 bytes UDP hdr, 4 bytes filler, 18 bytes "getserversResponse", 1 byte "\" */ + i+6 < ntohs(udph->len); + i+=7) { + DEBUGP("ip_nat_quake3: adding server at offset %u/%u %u.%u.%u.%u:%u\n", + i, ntohs(udph->len), + NIPQUAD( (u_int32_t) *( (u_int32_t *)( (int)udph + i ) ) ), + ntohs((__u16) *( (__u16 *)( (int)udph + i + 4 ) ) ) ); + + memset(&repl, 0, sizeof(repl)); + + repl.dst.protonum = IPPROTO_UDP; + repl.src.ip = ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.ip; + repl.dst.ip = *( (u_int32_t *)( (int)udph + i ) ); + repl.dst.u.udp.port = (__u16) *( (__u16 *)( (int)udph + i + 4 ) ); + + ip_conntrack_change_expect(exp, &repl); + } + } + return NF_ACCEPT; +} + +static unsigned int +quake3_nat_expected(struct sk_buff **pskb, + unsigned int hooknum, + struct ip_conntrack *ct, + struct ip_nat_info *info) +{ + const struct ip_conntrack *master = ct->master->expectant; + struct ip_nat_multi_range mr; + u_int32_t newsrcip, newdstip, newip; +#if 0 + const struct ip_conntrack_tuple *repl = + &master->tuplehash[IP_CT_DIR_REPLY].tuple; + struct iphdr *iph = (*pskb)->nh.iph; + struct udphdr *udph = (void *)iph + iph->ihl*4; +#endif + + DEBUGP("ip_nat_quake3: quake3_nat_expected: here we are\n"); + DUMP_TUPLE(&ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple); + + IP_NF_ASSERT(info); + IP_NF_ASSERT(master); + IP_NF_ASSERT(!(info->initialized & (1 << HOOK2MANIP(hooknum)))); + + newdstip = ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.ip; + newsrcip = master->tuplehash[IP_CT_DIR_REPLY].tuple.dst.ip; + + if (HOOK2MANIP(hooknum) == IP_NAT_MANIP_SRC) { + newip = newsrcip; + DEBUGP("hook: %s orig: %u.%u.%u.%u:%u <-> %u.%u.%u.%u:%u " + "newsrc: %u.%u.%u.%u\n", + hooknum == NF_IP_POST_ROUTING ? "POSTROUTING" + : hooknum == NF_IP_PRE_ROUTING ? "PREROUTING" + : hooknum == NF_IP_LOCAL_OUT ? "OUTPUT" : "????", + NIPQUAD((*pskb)->nh.iph->saddr), ntohs(udph->source), + NIPQUAD((*pskb)->nh.iph->daddr), ntohs(udph->dest), + NIPQUAD(newip)); + + } else { + newip = newdstip; + DEBUGP("hook: %s orig: %u.%u.%u.%u:%u <-> %u.%u.%u.%u:%u " + "newdst: %u.%u.%u.%u\n", + hooknum == NF_IP_POST_ROUTING ? "POSTROUTING" + : hooknum == NF_IP_PRE_ROUTING ? "PREROUTING" + : hooknum == NF_IP_LOCAL_OUT ? "OUTPUT" : "????", + NIPQUAD((*pskb)->nh.iph->saddr), ntohs(udph->source), + NIPQUAD((*pskb)->nh.iph->daddr), ntohs(udph->dest), + NIPQUAD(newip)); + } + + mr.rangesize = 1; + mr.range[0].flags = IP_NAT_RANGE_MAP_IPS; + mr.range[0].min_ip = mr.range[0].max_ip = newip; + + return ip_nat_setup_info(ct,&mr,hooknum); +} + +static struct ip_nat_helper quake3[MAX_PORTS]; +static char quake3_names[MAX_PORTS][13]; /* quake3-65535 */ + +static void fini(void) +{ + int i; + + for (i = 0 ; i < ports_c; i++) { + DEBUGP("ip_nat_quake3: unregistering helper for port %d\n", ports[i]); + ip_nat_helper_unregister(&quake3[i]); + } +} + +static int __init init(void) + { + int i, ret = 0; + char *tmpname; + + if (!ports[0]) + ports[0] = QUAKE3_MASTER_PORT; + + for (i = 0 ; (i < MAX_PORTS) && ports[i] ; i++) { + memset(&quake3[i], 0, sizeof(struct ip_nat_helper)); + + quake3[i].tuple.dst.protonum = IPPROTO_UDP; + quake3[i].tuple.src.u.udp.port = htons(ports[i]); + quake3[i].mask.dst.protonum = 0xFFFF; + quake3[i].mask.src.u.udp.port = 0xFFFF; + quake3[i].help = quake3_nat_help; + quake3[i].flags = 0; + quake3[i].me = THIS_MODULE; + quake3[i].expect = quake3_nat_expected; + + tmpname = &quake3_names[i][0]; + if (ports[i] == QUAKE3_MASTER_PORT) + sprintf(tmpname, "quake3"); + else + sprintf(tmpname, "quake3-%d", i); + quake3[i].name = tmpname; + + DEBUGP("ip_nat_quake3: registering helper for port %d: name %s\n", + ports[i], quake3[i].name); + ret = ip_nat_helper_register(&quake3[i]); + + if (ret) { + printk("ip_nat_quake3: unable to register helper for port %d\n", + ports[i]); + fini(); + return ret; + } + ports_c++; + } + return ret; + } + +module_init(init); +module_exit(fini); diff -Nur --exclude '*.orig' linux-2.6.6-rc1.org/net/ipv4/netfilter/ip_nat_standalone.c linux-2.6.6-rc1/net/ipv4/netfilter/ip_nat_standalone.c --- linux-2.6.6-rc1.org/net/ipv4/netfilter/ip_nat_standalone.c 2004-04-15 03:35:20.000000000 +0200 +++ linux-2.6.6-rc1/net/ipv4/netfilter/ip_nat_standalone.c 2004-04-16 09:17:11.000000000 +0200 @@ -175,6 +175,45 @@ return do_bindings(ct, ctinfo, info, hooknum, pskb); } +struct nat_route_key +{ + u_int32_t addr; +#ifdef CONFIG_XFRM + u_int16_t port; +#endif +}; + +static inline void +nat_route_key_get(struct sk_buff *skb, struct nat_route_key *key, int which) +{ + struct iphdr *iph = skb->nh.iph; + + key->addr = which ? iph->daddr : iph->saddr; +#ifdef CONFIG_XFRM + key->port = 0; + if (iph->protocol == IPPROTO_TCP || iph->protocol == IPPROTO_UDP) { + u_int16_t *ports = (u_int16_t *)(skb->nh.raw + iph->ihl*4); + key->port = ports[which]; + } +#endif +} + +static inline int +nat_route_key_compare(struct sk_buff *skb, struct nat_route_key *key, int which) +{ + struct iphdr *iph = skb->nh.iph; + + if (key->addr != (which ? iph->daddr : iph->saddr)) + return 1; +#ifdef CONFIG_XFRM + if (iph->protocol == IPPROTO_TCP || iph->protocol == IPPROTO_UDP) { + u_int16_t *ports = (u_int16_t *)(skb->nh.raw + iph->ihl*4); + if (key->port != ports[which]) + return 1; + } +#endif +} + static unsigned int ip_nat_out(unsigned int hooknum, struct sk_buff **pskb, @@ -182,6 +221,9 @@ const struct net_device *out, int (*okfn)(struct sk_buff *)) { + struct nat_route_key key; + unsigned int ret; + /* root is playing with raw sockets. */ if ((*pskb)->len < sizeof(struct iphdr) || (*pskb)->nh.iph->ihl * 4 < sizeof(struct iphdr)) @@ -204,7 +246,29 @@ return NF_STOLEN; } - return ip_nat_fn(hooknum, pskb, in, out, okfn); + nat_route_key_get(*pskb, &key, 0); + ret = ip_nat_fn(hooknum, pskb, in, out, okfn); + + if (ret != NF_DROP && ret != NF_STOLEN + && nat_route_key_compare(*pskb, &key, 0)) { + if (ip_route_me_harder(pskb) != 0) + ret = NF_DROP; +#ifdef CONFIG_XFRM + /* + * POST_ROUTING hook is called with fixed outfn, we need + * to manually confirm the packet and direct it to the + * transformers if a policy matches. + */ + else if ((*pskb)->dst->xfrm != NULL) { + ret = ip_conntrack_confirm(*pskb); + if (ret != NF_DROP) { + dst_output(*pskb); + ret = NF_STOLEN; + } + } +#endif + } + return ret; } #ifdef CONFIG_IP_NF_NAT_LOCAL @@ -215,7 +279,7 @@ const struct net_device *out, int (*okfn)(struct sk_buff *)) { - u_int32_t saddr, daddr; + struct nat_route_key key; unsigned int ret; /* root is playing with raw sockets. */ @@ -223,14 +287,14 @@ || (*pskb)->nh.iph->ihl * 4 < sizeof(struct iphdr)) return NF_ACCEPT; - saddr = (*pskb)->nh.iph->saddr; - daddr = (*pskb)->nh.iph->daddr; - + nat_route_key_get(*pskb, &key, 1); ret = ip_nat_fn(hooknum, pskb, in, out, okfn); + if (ret != NF_DROP && ret != NF_STOLEN - && ((*pskb)->nh.iph->saddr != saddr - || (*pskb)->nh.iph->daddr != daddr)) - return ip_route_me_harder(pskb) == 0 ? ret : NF_DROP; + && nat_route_key_compare(*pskb, &key, 1)) { + if (ip_route_me_harder(pskb) != 0) + ret = NF_DROP; + } return ret; } #endif diff -Nur --exclude '*.orig' linux-2.6.6-rc1.org/net/ipv4/netfilter/ip_nat_talk.c linux-2.6.6-rc1/net/ipv4/netfilter/ip_nat_talk.c --- linux-2.6.6-rc1.org/net/ipv4/netfilter/ip_nat_talk.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.6-rc1/net/ipv4/netfilter/ip_nat_talk.c 2004-04-16 09:18:58.000000000 +0200 @@ -0,0 +1,473 @@ +/* + * talk extension for UDP NAT alteration. + * Jozsef Kadlecsik + * + * 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. + ** + * Module load syntax: + * insmod ip_nat_talk.o talk=[0|1] ntalk=[0|1] ntalk2=[0|1] + * + * talk=[0|1] disable|enable old talk support + * ntalk=[0|1] disable|enable ntalk support + * ntalk2=[0|1] disable|enable ntalk2 support + * + * The default is talk=1 ntalk=1 ntalk2=1 + * + * + */ +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +/* Default all talk protocols are supported */ +static int talk = 1; +static int ntalk = 1; +static int ntalk2 = 1; +MODULE_AUTHOR("Jozsef Kadlecsik "); +MODULE_DESCRIPTION("talk network address translation module"); +#ifdef MODULE_PARM +MODULE_PARM(talk, "i"); +MODULE_PARM_DESC(talk, "support (old) talk protocol"); +MODULE_PARM(ntalk, "i"); +MODULE_PARM_DESC(ntalk, "support ntalk protocol"); +MODULE_PARM(ntalk2, "i"); +MODULE_PARM_DESC(ntalk2, "support ntalk2 protocol"); +#endif + +#if 0 +#define DEBUGP printk +#define IP_NAT_TALK_DEBUG +#else +#define DEBUGP(format, args...) +#endif + +/* FIXME: Time out? --RR */ + +static int +mangle_packet(struct sk_buff **pskb, + struct ip_conntrack *ct, + u_int32_t newip, + u_int16_t port, + struct talk_addr *addr, + struct talk_addr *ctl_addr) +{ + struct iphdr *iph = (*pskb)->nh.iph; + struct udphdr *udph = (void *)iph + iph->ihl * 4; + size_t udplen = (*pskb)->len - iph->ihl * 4; + + /* Fortunately talk sends a structure with the address and + port in it. The size of the packet won't change. */ + + if (ctl_addr == NULL) { + /* response */ + if (addr->ta_addr == INADDR_ANY) + return 1; + DEBUGP("ip_nat_talk_mangle_packet: response orig %u.%u.%u.%u:%u, inserting %u.%u.%u.%u:%u\n", + NIPQUAD(addr->ta_addr), ntohs(addr->ta_port), + NIPQUAD(newip), ntohs(port)); + addr->ta_addr = newip; + addr->ta_port = port; + } else { + /* message */ + if (addr->ta_addr != INADDR_ANY) { + /* Change address inside packet to match way we're mapping + this connection. */ + DEBUGP("ip_nat_talk_mangle_packet: message orig addr %u.%u.%u.%u:%u, inserting %u.%u.%u.%u:%u\n", + NIPQUAD(addr->ta_addr), ntohs(addr->ta_port), + NIPQUAD(ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.ip), + ntohs(addr->ta_port)); + addr->ta_addr = ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.ip; + } + DEBUGP("ip_nat_talk_mangle_packet: message orig ctl_addr %u.%u.%u.%u:%u, inserting %u.%u.%u.%u:%u\n", + NIPQUAD(ctl_addr->ta_addr), ntohs(ctl_addr->ta_port), + NIPQUAD(newip), ntohs(port)); + ctl_addr->ta_addr = newip; + ctl_addr->ta_port = port; + } + + /* Fix checksums */ + (*pskb)->csum = csum_partial((char *)udph + sizeof(struct udphdr), udplen - sizeof(struct udphdr), 0); + udph->check = 0; + udph->check = csum_tcpudp_magic(iph->saddr, iph->daddr, udplen, IPPROTO_UDP, + csum_partial((char *)udph, sizeof(struct udphdr), (*pskb)->csum)); + + ip_send_check(iph); + return 1; +} + +static int talk_help_msg(struct ip_conntrack *ct, + struct sk_buff **pskb, + u_char type, + struct talk_addr *addr, + struct talk_addr *ctl_addr) +{ + u_int32_t newip; + u_int16_t port; + + unsigned int verdict = NF_ACCEPT; + + DEBUGP("ip_nat_talk_help_msg: addr: %u.%u.%u.%u:%u, ctl_addr: %u.%u.%u.%u:%u, type %d\n", + NIPQUAD(addr->ta_addr), ntohs(addr->ta_port), + NIPQUAD(ctl_addr->ta_addr), ntohs(ctl_addr->ta_port), + type); + + /* Change address inside packet to match way we're mapping + this connection. */ + newip = ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.ip; + port = ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.u.udp.port; + DEBUGP("ip_nat_talk_help_msg: inserting: %u.%u.%u.%u:%u\n", + NIPQUAD(newip), ntohs(port)); + + if (!mangle_packet(pskb, ct, newip, port, addr, ctl_addr)) + verdict = NF_DROP; + + return verdict; +} + +static int talk_help_response(struct ip_conntrack *ct, + struct ip_conntrack_expect *exp, + struct sk_buff **pskb, + u_char type, + u_char answer, + struct talk_addr *addr) +{ + u_int32_t newip; + u_int16_t port; + struct ip_conntrack_tuple t; + struct ip_ct_talk_expect *ct_talk_info; + + DEBUGP("ip_nat_talk_help_response: addr: %u.%u.%u.%u:%u, type %d answer %d\n", + NIPQUAD(addr->ta_addr), ntohs(addr->ta_port), + type, answer); + + LOCK_BH(&ip_talk_lock); + ct_talk_info = &exp->help.exp_talk_info; + + if (!(answer == SUCCESS + && (type == LOOK_UP || type == ANNOUNCE) + && exp != NULL)) { + UNLOCK_BH(&ip_talk_lock); + return NF_ACCEPT; + } + + DEBUGP("ip_nat_talk_help_response: talkinfo port %u (%s)\n", + ntohs(ct_talk_info->port), + type == LOOK_UP ? "LOOK_UP" : "ANNOUNCE"); + + /* Change address inside packet to match way we're mapping + this connection. */ + newip = ct->tuplehash[type == LOOK_UP ? IP_CT_DIR_ORIGINAL : + IP_CT_DIR_REPLY].tuple.dst.ip; + /* We can read expect here without conntrack lock, since it's + only set in ip_conntrack_talk , with ip_talk_lock held + writable */ + t = exp->tuple; + t.dst.ip = newip; + + /* Try to get same port: if not, try to change it. */ + for (port = ntohs(ct_talk_info->port); port != 0; port++) { + if (type == LOOK_UP) + t.dst.u.tcp.port = htons(port); + else + t.dst.u.udp.port = htons(port); + + if (ip_conntrack_change_expect(exp, &t) == 0) { + DEBUGP("ip_nat_talk_help_response: using %u.%u.%u.%u:%u\n", NIPQUAD(newip), port); + break; + } + } + UNLOCK_BH(&ip_talk_lock); + + if (port == 0 || !mangle_packet(pskb, ct, newip, htons(port), addr, NULL)) + return NF_DROP; + + return NF_ACCEPT; +} + +static unsigned int talk_help(struct ip_conntrack *ct, + struct ip_conntrack_expect *exp, + struct ip_nat_info *info, + enum ip_conntrack_info ctinfo, + unsigned int hooknum, + struct sk_buff **pskb, + int talk_port) +{ + struct iphdr *iph = (*pskb)->nh.iph; + struct udphdr *udph = (void *)iph + iph->ihl * 4; + unsigned int udplen = (*pskb)->len - iph->ihl * 4; + char *data = (char *)udph + sizeof(struct udphdr); + int dir; + + /* Only mangle things once: original direction in POST_ROUTING + and reply direction on PRE_ROUTING. */ + dir = CTINFO2DIR(ctinfo); + if (!((hooknum == NF_IP_POST_ROUTING && dir == IP_CT_DIR_ORIGINAL) + || (hooknum == NF_IP_PRE_ROUTING && dir == IP_CT_DIR_REPLY))) { + DEBUGP("ip_nat_talk_help: Not touching dir %s at hook %s\n", + dir == IP_CT_DIR_ORIGINAL ? "ORIG" : "REPLY", + hooknum == NF_IP_POST_ROUTING ? "POSTROUTING" + : hooknum == NF_IP_PRE_ROUTING ? "PREROUTING" + : hooknum == NF_IP_LOCAL_OUT ? "OUTPUT" : "???"); + return NF_ACCEPT; + } + DEBUGP("ip_nat_talk_help: dir %s at hook %s, %u.%u.%u.%u:%u->%u.%u.%u.%u:%u, talk port %d\n", + dir == IP_CT_DIR_ORIGINAL ? "ORIG" : "REPLY", + hooknum == NF_IP_POST_ROUTING ? "POSTROUTING" + : hooknum == NF_IP_PRE_ROUTING ? "PREROUTING" + : hooknum == NF_IP_LOCAL_OUT ? "OUTPUT" : "???", + NIPQUAD(iph->saddr), ntohs(udph->source), + NIPQUAD(iph->daddr), ntohs(udph->dest), + talk_port); + + /* Because conntrack does not drop packets, checking must be repeated here... */ + if (talk_port == TALK_PORT) { + if (dir == IP_CT_DIR_ORIGINAL + && udplen == sizeof(struct udphdr) + sizeof(struct talk_msg)) + return talk_help_msg(ct, pskb, + ((struct talk_msg *)data)->type, + &(((struct talk_msg *)data)->addr), + &(((struct talk_msg *)data)->ctl_addr)); + else if (dir == IP_CT_DIR_REPLY + && udplen == sizeof(struct udphdr) + sizeof(struct talk_response)) + return talk_help_response(ct, exp, pskb, + ((struct talk_response *)data)->type, + ((struct talk_response *)data)->answer, + &(((struct talk_response *)data)->addr)); + else { + DEBUGP("ip_nat_talk_help: not talk %s, datalen %u != %u\n", + dir == IP_CT_DIR_ORIGINAL ? "message" : "response", + (unsigned)udplen - sizeof(struct udphdr), + dir == IP_CT_DIR_ORIGINAL ? sizeof(struct talk_msg) : sizeof(struct talk_response)); + return NF_DROP; + } + } else { + if (dir == IP_CT_DIR_ORIGINAL) { + if (ntalk + && udplen == sizeof(struct udphdr) + sizeof(struct ntalk_msg) + && ((struct ntalk_msg *)data)->vers == NTALK_VERSION) + return talk_help_msg(ct, pskb, + ((struct ntalk_msg *)data)->type, + &(((struct ntalk_msg *)data)->addr), + &(((struct ntalk_msg *)data)->ctl_addr)); + else if (ntalk2 + && udplen >= sizeof(struct udphdr) + sizeof(struct ntalk2_msg) + && ((struct ntalk2_msg *)data)->vers == NTALK2_VERSION + && udplen == sizeof(struct udphdr) + + sizeof(struct ntalk2_msg) + + ((struct ntalk2_msg *)data)->extended) + return talk_help_msg(ct, pskb, + ((struct ntalk2_msg *)data)->type, + &(((struct ntalk2_msg *)data)->addr), + &(((struct ntalk2_msg *)data)->ctl_addr)); + else { + DEBUGP("ip_nat_talk_help: not ntalk/ntalk2 message, datalen %u != %u or %u + max 256\n", + (unsigned)udplen - sizeof(struct udphdr), + sizeof(struct ntalk_msg), sizeof(struct ntalk2_msg)); + return NF_DROP; + } + } else { + if (ntalk + && udplen == sizeof(struct udphdr) + sizeof(struct ntalk_response) + && ((struct ntalk_response *)data)->vers == NTALK_VERSION) + return talk_help_response(ct, exp, pskb, + ((struct ntalk_response *)data)->type, + ((struct ntalk_response *)data)->answer, + &(((struct ntalk_response *)data)->addr)); + else if (ntalk2 + && udplen >= sizeof(struct udphdr) + sizeof(struct ntalk2_response) + && ((struct ntalk2_response *)data)->vers == NTALK2_VERSION) + return talk_help_response(ct, exp, pskb, + ((struct ntalk2_response *)data)->type, + ((struct ntalk2_response *)data)->answer, + &(((struct ntalk2_response *)data)->addr)); + else { + DEBUGP("ip_nat_talk_help: not ntalk/ntalk2 response, datalen %u != %u or %u + max 256\n", + (unsigned)udplen - sizeof(struct udphdr), + sizeof(struct ntalk_response), sizeof(struct ntalk2_response)); + return NF_DROP; + } + } + } +} + +static unsigned int help(struct ip_conntrack *ct, + struct ip_conntrack_expect *exp, + struct ip_nat_info *info, + enum ip_conntrack_info ctinfo, + unsigned int hooknum, + struct sk_buff **pskb) +{ + return talk_help(ct, exp, info, ctinfo, hooknum, pskb, TALK_PORT); +} + +static unsigned int nhelp(struct ip_conntrack *ct, + struct ip_conntrack_expect *exp, + struct ip_nat_info *info, + enum ip_conntrack_info ctinfo, + unsigned int hooknum, + struct sk_buff **pskb) +{ + return talk_help(ct, exp, info, ctinfo, hooknum, pskb, NTALK_PORT); +} + +static unsigned int +talk_nat_expected(struct sk_buff **pskb, + unsigned int hooknum, + struct ip_conntrack *ct, + struct ip_nat_info *info); + +static struct ip_nat_helper talk_helpers[2] = + { { { NULL, NULL }, + "talk", /* name */ + IP_NAT_HELPER_F_ALWAYS, /* flags */ + THIS_MODULE, /* module */ + { { 0, { .udp = { __constant_htons(TALK_PORT) } } }, /* tuple */ + { 0, { 0 }, IPPROTO_UDP } }, + { { 0, { .udp = { 0xFFFF } } }, /* mask */ + { 0, { 0 }, 0xFFFF } }, + help, /* helper */ + talk_nat_expected }, /* expectfn */ + { { NULL, NULL }, + "ntalk", /* name */ + IP_NAT_HELPER_F_ALWAYS, /* flags */ + THIS_MODULE, /* module */ + { { 0, { .udp = { __constant_htons(NTALK_PORT) } } }, /* tuple */ + { 0, { 0 }, IPPROTO_UDP } }, + { { 0, { .udp = { 0xFFFF } } }, /* mask */ + { 0, { 0 }, 0xFFFF } }, + nhelp, /* helper */ + talk_nat_expected } /* expectfn */ + }; + +static unsigned int +talk_nat_expected(struct sk_buff **pskb, + unsigned int hooknum, + struct ip_conntrack *ct, + struct ip_nat_info *info) +{ + struct ip_nat_multi_range mr; + u_int32_t newdstip, newsrcip, newip; + u_int16_t port; + unsigned int ret; + + struct ip_conntrack *master = master_ct(ct); + + IP_NF_ASSERT(info); + IP_NF_ASSERT(master); + + IP_NF_ASSERT(!(info->initialized & (1<master->help.exp_talk_info.port; + UNLOCK_BH(&ip_talk_lock); + + DEBUGP("ip_nat_talk_expected: dir %s at hook %s, ct %p, master %p\n", + CTINFO2DIR((*pskb)->nfct - ct->infos) == IP_CT_DIR_ORIGINAL ? "ORIG" : "REPLY", + hooknum == NF_IP_POST_ROUTING ? "POSTROUTING" + : hooknum == NF_IP_PRE_ROUTING ? "PREROUTING" + : hooknum == NF_IP_LOCAL_OUT ? "OUTPUT" : "???", + ct, master); + + if (ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.protonum == IPPROTO_UDP) { + /* Callee client -> caller server */ +#ifdef IP_NAT_TALK_DEBUG + struct iphdr *iph = (*pskb)->nh.iph; + struct udphdr *udph = (void *)iph + iph->ihl * 4; + + DEBUGP("ip_nat_talk_expected: UDP %u.%u.%u.%u:%u->%u.%u.%u.%u:%u\n", + NIPQUAD(iph->saddr), ntohs(udph->source), + NIPQUAD(iph->daddr), ntohs(udph->dest)); +#endif + newdstip = master->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.ip; + newsrcip = master->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.ip; + DEBUGP("ip_nat_talk_expected: callee client -> caller server, newsrc: %u.%u.%u.%u, newdst: %u.%u.%u.%u\n", + NIPQUAD(newsrcip), NIPQUAD(newdstip)); + } else { + /* Callee client -> caller client */ +#ifdef IP_NAT_TALK_DEBUG + struct iphdr *iph = (*pskb)->nh.iph; + struct tcphdr *tcph = (void *)iph + iph->ihl * 4; + + DEBUGP("ip_nat_talk_expected: TCP %u.%u.%u.%u:%u->%u.%u.%u.%u:%u\n", + NIPQUAD(iph->saddr), ntohs(tcph->source), + NIPQUAD(iph->daddr), ntohs(tcph->dest)); +#endif + newdstip = master->tuplehash[IP_CT_DIR_REPLY].tuple.src.ip; + newsrcip = master->tuplehash[IP_CT_DIR_REPLY].tuple.dst.ip; + DEBUGP("ip_nat_talk_expected: callee client -> caller client, newsrc: %u.%u.%u.%u, newdst: %u.%u.%u.%u\n", + NIPQUAD(newsrcip), NIPQUAD(newdstip)); + } + if (HOOK2MANIP(hooknum) == IP_NAT_MANIP_SRC) + newip = newsrcip; + else + newip = newdstip; + + DEBUGP("ip_nat_talk_expected: IP to %u.%u.%u.%u, port %u\n", NIPQUAD(newip), ntohs(port)); + + mr.rangesize = 1; + /* We don't want to manip the per-protocol, just the IPs... */ + mr.range[0].flags = IP_NAT_RANGE_MAP_IPS; + mr.range[0].min_ip = mr.range[0].max_ip = newip; + + /* ... unless we're doing a MANIP_DST, in which case, make + sure we map to the correct port */ + if (HOOK2MANIP(hooknum) == IP_NAT_MANIP_DST) { + mr.range[0].flags |= IP_NAT_RANGE_PROTO_SPECIFIED; + mr.range[0].min = mr.range[0].max + = ((union ip_conntrack_manip_proto) + { .udp = { port } }); + } + ret = ip_nat_setup_info(ct, &mr, hooknum); + + if (ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.protonum == IPPROTO_UDP) { + DEBUGP("talk_expected: setting NAT helper for %p\n", ct); + /* NAT expectfn called with ip_nat_lock write-locked */ + info->helper = &talk_helpers[htons(port) - TALK_PORT]; + } + return ret; +} + +static int __init init(void) +{ + int ret = 0; + + if (talk > 0) { + ret = ip_nat_helper_register(&talk_helpers[0]); + + if (ret != 0) + return ret; + } + if (ntalk > 0 || ntalk2 > 0) { + ret = ip_nat_helper_register(&talk_helpers[1]); + + if (ret != 0 && talk > 0) + ip_nat_helper_unregister(&talk_helpers[0]); + } + return ret; +} + +static void __exit fini(void) +{ + if (talk > 0) + ip_nat_helper_unregister(&talk_helpers[0]); + if (ntalk > 0 || ntalk2 > 0) + ip_nat_helper_unregister(&talk_helpers[1]); +} + +module_init(init); +module_exit(fini); 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-16 08:59:08.000000000 +0200 +++ linux-2.6.6-rc1/net/ipv4/netfilter/ip_tables.c 2004-04-16 09:17:31.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-16 09:15:41.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-16 09:15:43.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_ROUTE.c linux-2.6.6-rc1/net/ipv4/netfilter/ipt_ROUTE.c --- linux-2.6.6-rc1.org/net/ipv4/netfilter/ipt_ROUTE.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.6-rc1/net/ipv4/netfilter/ipt_ROUTE.c 2004-04-16 09:15:44.000000000 +0200 @@ -0,0 +1,369 @@ +/* + * This implements the ROUTE target, which enables you to setup unusual + * routes not supported by the standard kernel routing table. + * + * Copyright (C) 2002 Cedric de Launois + * + * v 1.8 2003/07/25 + * + * This software is distributed under GNU GPL v2, 1991 + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if 0 +#define DEBUGP printk +#else +#define DEBUGP(format, args...) +#endif + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Cedric de Launois "); + +/* Try to route the packet according to the routing keys specified in + * route_info. Keys are : + * - ifindex : + * 0 if no oif preferred, + * otherwise set to the index of the desired oif + * - route_info->gw : + * 0 if no gateway specified, + * otherwise set to the next host to which the pkt must be routed + * If success, skb->dev is the output device to which the packet must + * be sent and skb->dst is not NULL + * + * RETURN: -1 if an error occured + * 1 if the packet was succesfully routed to the + * destination desired + * 0 if the kernel routing table could not route the packet + * according to the keys specified + */ +static int route(struct sk_buff *skb, + unsigned int ifindex, + const struct ipt_route_target_info *route_info) +{ + int err; + struct rtable *rt; + struct iphdr *iph = skb->nh.iph; + struct rt_key key = { + dst:iph->daddr, + src:0, + oif:ifindex, + tos:RT_TOS(iph->tos) + }; + + /* The destination address may be overloaded by the target */ + if (route_info->gw) + key.dst = route_info->gw; + + /* Trying to route the packet using the standard routing table. */ + if ((err = ip_route_output_key(&rt, &key))) { + if (net_ratelimit()) + DEBUGP("ipt_ROUTE: couldn't route pkt (err: %i)",err); + return -1; + } + + /* Drop old route. */ + dst_release(skb->dst); + skb->dst = NULL; + + /* Success if no oif specified or if the oif correspond to the + * one desired */ + if (!ifindex || rt->u.dst.dev->ifindex == ifindex) { + skb->dst = &rt->u.dst; + skb->dev = skb->dst->dev; + return 1; + } + + /* The interface selected by the routing table is not the one + * specified by the user. This may happen because the dst address + * is one of our own addresses. + */ + if (net_ratelimit()) + DEBUGP("ipt_ROUTE: failed to route as desired gw=%u.%u.%u.%u oif=%i (got oif=%i)\n", + NIPQUAD(route_info->gw), ifindex, rt->u.dst.dev->ifindex); + + return 0; +} + + +/* Stolen from ip_finish_output2 + * PRE : skb->dev is set to the device we are leaving by + * skb->dst is not NULL + * POST: the packet is sent with the link layer header pushed + * the packet is destroyed + */ +static void 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); + hh->hh_output(skb); + } else if (dst->neighbour) + dst->neighbour->output(skb); + else { + if (net_ratelimit()) + DEBUGP(KERN_DEBUG "ipt_ROUTE: no hdr & no neighbour cache!\n"); + kfree_skb(skb); + } +} + + +/* PRE : skb->dev is set to the device we are leaving by + * POST: - the packet is directly sent to the skb->dev device, without + * pushing the link layer header. + * - the packet is destroyed + */ +static inline int dev_direct_send(struct sk_buff *skb) +{ + return dev_queue_xmit(skb); +} + + +static unsigned int route_oif(const struct ipt_route_target_info *route_info, + struct sk_buff *skb) +{ + unsigned int ifindex = 0; + struct net_device *dev_out = NULL; + + /* The user set the interface name to use. + * Getting the current interface index. + */ + if ((dev_out = dev_get_by_name(route_info->oif))) { + ifindex = dev_out->ifindex; + } else { + /* Unknown interface name : packet dropped */ + if (net_ratelimit()) + DEBUGP("ipt_ROUTE: oif interface %s not found\n", route_info->oif); + return NF_DROP; + } + + /* Trying the standard way of routing packets */ + switch (route(skb, ifindex, route_info)) { + case 1: + dev_put(dev_out); + if (route_info->flags & IPT_ROUTE_CONTINUE) + return IPT_CONTINUE; + + ip_direct_send(skb); + return NF_STOLEN; + + case 0: + /* Failed to send to oif. Trying the hard way */ + if (route_info->flags & IPT_ROUTE_CONTINUE) + return NF_DROP; + + if (net_ratelimit()) + DEBUGP("ipt_ROUTE: forcing the use of %i\n", + ifindex); + + /* We have to force the use of an interface. + * This interface must be a tunnel interface since + * otherwise we can't guess the hw address for + * the packet. For a tunnel interface, no hw address + * is needed. + */ + if ((dev_out->type != ARPHRD_TUNNEL) + && (dev_out->type != ARPHRD_IPGRE)) { + if (net_ratelimit()) + DEBUGP("ipt_ROUTE: can't guess the hw addr !\n"); + dev_put(dev_out); + return NF_DROP; + } + + /* Send the packet. This will also free skb + * Do not go through the POST_ROUTING hook because + * skb->dst is not set and because it will probably + * get confused by the destination IP address. + */ + skb->dev = dev_out; + dev_direct_send(skb); + dev_put(dev_out); + return NF_STOLEN; + + default: + /* Unexpected error */ + dev_put(dev_out); + return NF_DROP; + } +} + + +static unsigned int route_iif(const struct ipt_route_target_info *route_info, + struct sk_buff *skb) +{ + struct net_device *dev_out = NULL; + unsigned int ifindex = 0; + + /* Getting the current interface index. */ + if ((dev_out = dev_get_by_name(route_info->iif))) + ifindex = dev_out->ifindex; + else { + /* Unknown interface name : packet dropped */ + if (net_ratelimit()) + DEBUGP("ipt_ROUTE: iif interface %s not found\n", route_info->oif); + return NF_DROP; + } + + skb->dev = dev_out; + dst_release(skb->dst); + skb->dst = NULL; + + netif_rx(skb); + + return NF_STOLEN; +} + + +static unsigned int route_gw(const struct ipt_route_target_info *route_info, + struct sk_buff *skb) +{ + if (route(skb, 0, route_info)!=1) + return NF_DROP; + + if (route_info->flags & IPT_ROUTE_CONTINUE) + return IPT_CONTINUE; + + ip_direct_send(skb); + return NF_STOLEN; +} + + +static unsigned int ipt_route_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_route_target_info *route_info = targinfo; + struct sk_buff *skb = *pskb; + + /* If we are at PREROUTING or INPUT hook + * the TTL isn't decreased by the IP stack + */ + if (hooknum == NF_IP_PRE_ROUTING || + hooknum == NF_IP_LOCAL_IN) { + + struct iphdr *iph = skb->nh.iph; + + if (iph->ttl <= 1) { + struct rtable *rt; + + if (ip_route_output(&rt, iph->saddr, iph->daddr, + RT_TOS(iph->tos) | RTO_CONN, + 0)) { + return NF_DROP; + } + + if (skb->dev == rt->u.dst.dev) { + /* Drop old route. */ + dst_release(skb->dst); + skb->dst = &rt->u.dst; + + /* this will traverse normal stack, and + * thus call conntrack on the icmp packet */ + icmp_send(skb, ICMP_TIME_EXCEEDED, + ICMP_EXC_TTL, 0); + } + + return NF_DROP; + } + + ip_decrease_ttl(iph); + } + + /* Tell conntrack to forget this packet since it may get confused + * when a packet is leaving with dst address == our address. + * Good idea ? Dunno. Need advice. + */ + if (!(route_info->flags & IPT_ROUTE_CONTINUE)) { + nf_conntrack_put(skb->nfct); + skb->nfct = NULL; + skb->nfcache = 0; +#ifdef CONFIG_NETFILTER_DEBUG + skb->nf_debug = 0; +#endif + } + + if (route_info->oif[0]) + return route_oif(route_info, *pskb); + + if (route_info->iif[0]) + return route_iif(route_info, *pskb); + + if (route_info->gw) + return route_gw(route_info, *pskb); + + if (net_ratelimit()) + DEBUGP(KERN_DEBUG "ipt_ROUTE: no parameter !\n"); + + return IPT_CONTINUE; +} + + +static int ipt_route_checkentry(const char *tablename, + const struct ipt_entry *e, + void *targinfo, + unsigned int targinfosize, + unsigned int hook_mask) +{ + if (strcmp(tablename, "mangle") != 0) { + printk("ipt_ROUTE: bad table `%s', use the `mangle' table.\n", + tablename); + return 0; + } + + if (hook_mask & ~( (1 << NF_IP_PRE_ROUTING) + | (1 << NF_IP_LOCAL_IN) + | (1 << NF_IP_FORWARD) + | (1 << NF_IP_LOCAL_OUT) + | (1 << NF_IP_POST_ROUTING))) { + printk("ipt_ROUTE: bad hook\n"); + return 0; + } + + if (targinfosize != IPT_ALIGN(sizeof(struct ipt_route_target_info))) { + printk(KERN_WARNING "ipt_ROUTE: targinfosize %u != %Zu\n", + targinfosize, + IPT_ALIGN(sizeof(struct ipt_route_target_info))); + return 0; + } + + return 1; +} + + +static struct ipt_target ipt_route_reg = { + .name = "ROUTE", + .target = ipt_route_target, + .checkentry = ipt_route_checkentry, + .me = THIS_MODULE, +}; + +static int __init init(void) +{ + return ipt_register_target(&ipt_route_reg); +} + + +static void __exit fini(void) +{ + ipt_unregister_target(&ipt_route_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-16 09:19:08.000000000 +0200 @@ -0,0 +1,290 @@ +/* + * 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 + + +/* 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; + struct flowi fl = {}; + + /* 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); + + fl.nl_u.ip4_u.daddr = nskb->nh.iph->daddr; + fl.nl_u.ip4_u.saddr = (local ? nskb->nh.iph->saddr : 0); + fl.nl_u.ip4_u.tos = RT_TOS(nskb->nh.iph->tos) | RTO_CONN; + fl.oif = 0; + if (ip_route_output_key(&nrt, &fl) != 0) + goto free_nskb; + + dst_release(nskb->dst); + nskb->dst = &nrt->u.dst; + + /* "Never happens" */ + if (nskb->len > dst_pmtu(nskb->dst)) + goto free_nskb; + + ip_direct_send (nskb); + + return; + + free_nskb: + kfree_skb(nskb); +} + + +static unsigned int tarpit(struct sk_buff **pskb, + unsigned int hooknum, + const struct net_device *in, + const struct net_device *out, + 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) +{ + if (ipt_register_target(&ipt_tarpit_reg)) + return -EINVAL; + return 0; +} + +static void __exit fini(void) +{ + ipt_unregister_target(&ipt_tarpit_reg); +} + +module_init(init); +module_exit(fini); +MODULE_LICENSE("GPL"); diff -Nur --exclude '*.orig' linux-2.6.6-rc1.org/net/ipv4/netfilter/ipt_TCPLAG.c linux-2.6.6-rc1/net/ipv4/netfilter/ipt_TCPLAG.c --- linux-2.6.6-rc1.org/net/ipv4/netfilter/ipt_TCPLAG.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.6-rc1/net/ipv4/netfilter/ipt_TCPLAG.c 2004-04-16 09:15:54.000000000 +0200 @@ -0,0 +1,697 @@ +/* ipt_TCPLAG.c -- kernel module to implement TCPLAG target into netfilter + * Copyright (C) 2002 Telford Tendys + * + * 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 + */ + +/* + * This collects packets and attempts to make them into pairs + * based on its own knowledge of how typical network conversations + * operate. Once it has a pair, it logs the time between them. + */ +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#if 0 +#define DEBUGP printk +#else +#define DEBUGP(format, args...) +#endif + +/* + * We need one spinlock for the hash table. + */ +static spinlock_t hash_lock = SPIN_LOCK_UNLOCKED; + +typedef struct timeval timeval_T; + +/* + * Linked lists of events in the connection, + * these store the SEQ numbers and the newest is always + * at the start of the linked list, then they get older + * down to the end of the linked list (this is not perfect + * if packets get out of order but we don't worry about fine + * details like that). + * + * Matching any event wipes out that event and also all other + * events down the chain (i.e. all older events). + * This keeps the linked list as short as possible. + */ +typedef struct tcplag_event_S +{ + struct tcplag_event_S *next; + u16 source_port; + u16 dest_port; + u32 expected_ACK; + struct timeval stamp; +} tcplag_event_T; + +/* + * This stores the connection statistics + * We define connections more loosely than TCP/IP does, + * because we only consider the two hosts, not the ports + * Also, we list the host-pairs in low,high order which + * means that we don't care who originated the connection. + */ +typedef struct tcplag_hash_S +{ + u32 low_ip; + u32 high_ip; + struct timeval lag_l_SEQ_h_ACK; /* l sends some data and h acknowledges that (sum of lag times) */ + struct timeval lag_h_SEQ_l_ACK; /* h sends some data and l acknowledges that (sum of lag times) */ + tcplag_event_T *h_ACK_list; /* Try to match ACK packets coming from h in this list */ + tcplag_event_T *l_ACK_list; /* Try to match ACK packets coming from l in this list */ + time_t stamp; /* When this bucket got added to the table */ + u16 count_l_SEQ_h_ACK; /* Increment for each event */ + u16 count_h_SEQ_l_ACK; /* Increment for each event */ +} tcplag_hash_T; + +static tcplag_hash_T **hashtab = 0; +static u32 hashsize = 0; +static u16 max_seconds = 30; /* Empty a hash bucket after this time */ +static u32 reaper_ix = 0; + +static void divide_down( timeval_T *T, int c ) +{ + int remainder; + + T->tv_usec /= c; + remainder = T->tv_sec % c; /* Only works properly with positive numbers */ + remainder *= 1000000; + T->tv_usec == remainder; + T->tv_sec /= c; +} + +int diff_timeval( timeval_T *tv1, timeval_T *tv2 ) +{ + register long x; + + x = tv1->tv_sec - tv2->tv_sec; + if( x ) return( x ); + x = tv1->tv_usec - tv2->tv_usec; + return( x ); +} + +void sprint_timeval( char *buf, timeval_T *tv ) +{ + if( tv->tv_sec ) + sprintf( buf, "%lu%06lu", tv->tv_sec, tv->tv_usec ); + else + sprintf( buf, "%lu", tv->tv_usec ); +} + +/* + * This generates the log messages through printk() + * + * There is really no particular interest in the port numbers at this stage, + * they are only useful for matching up the request with the reply. + * The IP numbers are useful because some sites may be slower than others + * or may travel different routes, etc (OK, in theory changing the port number + * could also change the route but I don't like that sort of theory). + * + * The tags are: + * + * LIP= The IP number of the side with the lowest lag + * RIP= The IP number of the side with the highest lag + * LLAG= The average time (in us) between RIP->LIP SEQ and LIP->RIP ACK + * RLAG= The average time (in us) between LIP->RIP SEQ and RIP->LIP ACK + */ +static void output( tcplag_hash_T *H, int level, const char *prefix ) +{ + struct timeval ltm, rtm; + u32 local_ip, remote_ip; + char r_buf[ 20 ], l_buf[ 20 ]; +/* + * We can't make sense of a connection that only passes data one way, + * In principle, at least the SYN and FIN should go both ways so we + * should get a few hits for every connection. + */ + if( 0 == H->count_l_SEQ_h_ACK || 0 == H->count_h_SEQ_l_ACK ) return; +/* + * Calculate average times by dividing down + */ + divide_down( &H->lag_l_SEQ_h_ACK, H->count_l_SEQ_h_ACK ); + divide_down( &H->lag_h_SEQ_l_ACK, H->count_h_SEQ_l_ACK ); +/* + * Sort these two by the lag so the the local is always the short lag + */ + if( diff_timeval( &H->lag_l_SEQ_h_ACK, &H->lag_h_SEQ_l_ACK ) > 0 ) + { + local_ip = H->low_ip; + remote_ip = H->high_ip; + rtm.tv_sec = H->lag_l_SEQ_h_ACK.tv_sec; + rtm.tv_usec = H->lag_l_SEQ_h_ACK.tv_usec; + ltm.tv_sec = H->lag_h_SEQ_l_ACK.tv_sec; + ltm.tv_usec = H->lag_h_SEQ_l_ACK.tv_usec; + } + else + { + local_ip = H->high_ip; + remote_ip = H->low_ip; + ltm.tv_sec = H->lag_l_SEQ_h_ACK.tv_sec; + ltm.tv_usec = H->lag_l_SEQ_h_ACK.tv_usec; + rtm.tv_sec = H->lag_h_SEQ_l_ACK.tv_sec; + rtm.tv_usec = H->lag_h_SEQ_l_ACK.tv_usec; + } +/* + * Don't use a spinlock on the output, + * it is not guaranteed safe because some OTHER printk could + * split our log message so we want only one single printk. + * + * We use sprintf() to partially pre-digest the output + * + * Actually, neither this not the main netfilter LOG target is + * really safe from printk() overlap, basically syslog cannot + * be regarded as a guaranteed data output channel. It is good + * enough for most purposes. + */ + sprint_timeval( l_buf, <m ); + sprint_timeval( r_buf, &rtm ); + printk( "<%d>%sLIP=%u.%u.%u.%u RIP=%u.%u.%u.%u LLAG=%s RLAG=%s\n", + level & 7, prefix, + NIPQUAD( local_ip ), NIPQUAD( remote_ip ), + l_buf, r_buf ); +} + +/* + * The reaper rolls through the hash table looking for old. + * Log entries are only generated at the reaping time + * (which means all log entries are out-of-date) + */ +static void reaper( time_t now, int level, const char *prefix ) +{ + int i; + + now -= max_seconds; + if( !hashsize ) return; + if( !hashtab ) return; + for( i = 0; i < 10; i++ ) + { + if( ++reaper_ix >= hashsize ) reaper_ix = 0; + +// DEBUGP( KERN_WARNING "reaper checking %u\n", reaper_ix ); + + if( hashtab[ reaper_ix ]) + { + tcplag_hash_T *found = 0; + + spin_lock_bh( &hash_lock ); + if( hashtab[ reaper_ix ]) + { + if( now > hashtab[ reaper_ix ]->stamp ) + { + DEBUGP( KERN_WARNING "reaper found expired entry\n" ); + found = hashtab[ reaper_ix ]; + hashtab[ reaper_ix ] = 0; + } + } + spin_unlock_bh( &hash_lock ); + + if( found ) + { + output( found, level, prefix ); + kfree( found ); + } + } + } +} + +/* + * Convert the connection characteristics into a number + * (not including the timestamp) FIXME: this is a sucky hash function + */ +static u32 make_hash( tcplag_hash_T *connection ) +{ + register u32 r; + + r = connection->low_ip; + r += connection->high_ip; + return( r ); +} + +static int compare_connections( tcplag_hash_T *con1, tcplag_hash_T *con2 ) +{ + int x; + + x = con1->low_ip - con2->low_ip; if( x ) return( x ); + x = con1->high_ip - con2->high_ip; + return( x ); +} + +static int compare_events( tcplag_event_T *ev1, tcplag_event_T *ev2 ) +{ + int x; + + DEBUGP( "Comparing sequence %u to %u\n", ev1->expected_ACK, ev2->expected_ACK ); + x = ev1->expected_ACK - ev2->expected_ACK; + if( x ) return( x ); + DEBUGP( "Comparing source port %u to %u\n", ev1->source_port, ev2->source_port ); + x = ev1->source_port - ev2->source_port; + if( x ) return( x ); + DEBUGP( "Comparing destination port %u to %u\n", ev1->dest_port, ev2->dest_port ); + x = ev1->dest_port - ev2->dest_port; + return( x ); +} + +/* + * Go to the hash table and either find an existing connection that + * matches correctly or inject a new connection into the table. + * Once the connection is OK, chain the event onto the linked list. + */ +static void hash_insert( tcplag_hash_T *connection, tcplag_event_T *event, int direction ) +{ + u32 h, i; + + if( !event ) return; /* Just to be safe */ + if( !hashsize ) return; + if( !hashtab ) return; + + h = make_hash( connection ); + h %= hashsize; + + DEBUGP( KERN_WARNING "hash_insert( %u )\n", h ); + + spin_lock_bh( &hash_lock ); + for( i = 0; i < hashsize; i++, ({ if( ++h >= hashsize ) { h = 0; }})) + { + tcplag_hash_T *co_new = 0; +/* + * Consider existing entry + */ + if( hashtab[ h ]) + { + if( compare_connections( hashtab[ h ], connection )) continue; + co_new = hashtab[ h ]; + DEBUGP( KERN_WARNING "Existing connection at %u\n", h ); + goto add_link; + } +/* + * Use empty slot for new entry + */ + if( !hashtab[ h ]) + { + co_new = kmalloc( sizeof( tcplag_hash_T ), GFP_ATOMIC ); + memset( co_new, 0, sizeof( tcplag_hash_T )); + co_new->low_ip = connection->low_ip; + co_new->high_ip = connection->high_ip; + co_new->stamp = event->stamp.tv_sec; + hashtab[ h ] = co_new; + DEBUGP( KERN_WARNING "Added connection to table at %u\n", h ); + add_link: + { + tcplag_event_T *ev_new; + + ev_new = kmalloc( sizeof( tcplag_event_T ), GFP_ATOMIC ); + memcpy( ev_new, event, sizeof( tcplag_event_T )); + if( direction ) + { + ev_new->next = co_new->h_ACK_list; + co_new->h_ACK_list = ev_new; + DEBUGP( KERN_WARNING "Connection at %u, direction is h_ACK_list\n", h ); + } + else + { + ev_new->next = co_new->l_ACK_list; + co_new->l_ACK_list = ev_new; + DEBUGP( KERN_WARNING "Connection at %u, direction is l_ACK_list\n", h ); + } + } + goto done; + } + } + done: + spin_unlock_bh( &hash_lock ); +} + +/* + * Search the hash table for a matching connection, + * if we can't find one of those then we are stuffed. + * + * Once a connection has been found, scan along the list for + * a matching SEQ number and if that is found then calculate + * the lag, update the counters and cut the chain at the + * point where the matching SEQ is found. + */ +static int request_complete( tcplag_hash_T *connection, tcplag_event_T *event, int direction ) +{ + u32 h, i; + + if( !event ) return( 0 ); + if( !hashsize ) return( 0 ); + if( !hashtab ) return( 0 ); + h = make_hash( connection ); + h %= hashsize; + + DEBUGP( KERN_WARNING "request_complete( %u )\n", h ); + + for( i = 0; i < hashsize; i++ ) + { + tcplag_hash_T *found = 0; + + if( !hashtab[ h ]) return( 0 ); + + spin_lock_bh( &hash_lock ); + if( hashtab[ h ]) + { + if( !compare_connections( hashtab[ h ], connection )) + { + tcplag_event_T *ev, **evroot; + timeval_T *tv; + u16 *cn; + + found = hashtab[ h ]; + if( direction ) + { + evroot = &found->h_ACK_list; + tv = &found->lag_l_SEQ_h_ACK; + cn = &found->count_l_SEQ_h_ACK; + DEBUGP( KERN_WARNING "Connection at %u, direction is h_ACK_list\n", h ); + } + else + { + evroot = &found->l_ACK_list; + tv = &found->lag_h_SEQ_l_ACK; + cn = &found->count_h_SEQ_l_ACK; + DEBUGP( KERN_WARNING "Connection at %u, direction is l_ACK_list\n", h ); + } + for( ev = *evroot; ev; ev = ev->next ) + { + if( !compare_events( ev, event )) + { +/* + * Calculate the lag (in two parts) and add that to the collection + */ + event->stamp.tv_sec -= ev->stamp.tv_sec; + event->stamp.tv_usec -= ev->stamp.tv_usec; + if( event->stamp.tv_usec < 0 ) + { + event->stamp.tv_usec += 1000000; + event->stamp.tv_sec++; + } + if( event->stamp.tv_sec < 0 ) + { + DEBUGP( KERN_WARNING "Negative lag detected\n" ); + } + else + { + tv->tv_sec += event->stamp.tv_sec; + tv->tv_usec += event->stamp.tv_usec; + ++*cn; + DEBUGP( KERN_WARNING "Found a match, added %lu.%06lu" + " (accumulator is up to %lu.%06lu, %u events)\n", + event->stamp.tv_sec, + event->stamp.tv_usec, + tv->tv_sec, tv->tv_usec, *cn ); + } +/* + * Truncate the linked list. + * + * Visit each event in the list and return the memory to the pool. + * + * If a host is making multiple connections to the same remote host + * then this truncation will result in some requests not being + * monitored. Statistically we will still get some reasonable number + * of measurements and multiple simultaneous connections between host + * pairs don't happen all that often. + */ + *evroot = 0; + while( ev ) + { + tcplag_event_T *ev_next = ev->next; + DEBUGP( KERN_WARNING "Shitcan %u\n", ev->expected_ACK ); + kfree( ev ); + ev = ev_next; + } +/* + * TODO: overflow limit for *cn, force premature output() if necessary + * (and drop this connection from the hash table) + */ + break; + } + } + goto done; + } + } + done: + spin_unlock_bh( &hash_lock ); + + if( found ) return( 1 ); + if( ++h >= hashsize ) h = 0; + } + return( 0 ); +} + +/* + * Here is our target data: + * + * pskb -- The packet itself (see linux/skbuff.h for breakdown) + * + * hooknum -- + * + * in -- The device that this packet came in on + * (depending on the chain this may or may not exist) + * + * out -- The device that this packet is just about to go + * out onto (again existance depends on the chain) + * + * targinfo -- Our private data (handed through from iptables command util) + * + * userinfo -- Some more data + * + */ + +static unsigned int target( struct sk_buff **pskb, + unsigned int hooknum, + const struct net_device *in, + const struct net_device *out, + const void *targinfo, + void *userinfo ) +{ + struct iphdr *iph = ( *pskb )->nh.iph; + const struct ipt_tcplag *el = targinfo; + tcplag_hash_T connection; + tcplag_event_T event; + int direction; +/* + * We know we are dealing with IP here + * Fill in all the obvious fields + */ + if( iph->saddr > iph->daddr ) + { + direction = 0; + connection.high_ip = iph->saddr; + connection.low_ip = iph->daddr; + } + else + { + direction = 1; + connection.low_ip = iph->saddr; + connection.high_ip = iph->daddr; + } + do_gettimeofday( &event.stamp ); +/* + * Do a bit of cleaning + */ + reaper( event.stamp.tv_sec, el->level, el->prefix ); + + DEBUGP( KERN_WARNING "got packet %lu %lu %s %s\n", + event.stamp.tv_sec, + event.stamp.tv_usec, + in ? in->name : "none", out ? out->name : "none" ); +/* + * Now start looking at the details + * + * First step is to identify this packet to see if it is + * the sort of packet that we are interested in. + * Don't hold any locks while we are doing this because often + * we will just let the packet go without any further consideration. + */ + switch( iph->protocol ) + { + case IPPROTO_TCP: + { + struct tcphdr *tcp; + + if( ntohs( iph->frag_off ) & IP_OFFSET ) + { + DEBUGP( KERN_WARNING "ignoring fragment\n" ); + break; + } + tcp = (struct tcphdr *)((u32 *)iph + iph->ihl ); + event.source_port = ntohs( tcp->source ); + event.dest_port = ntohs( tcp->dest ); +/* + * Every packet should have a valid SEQ number so use this to + * generate an ACK number. This works along the formula: + * -- Start with the SEQ number + * -- For SYN or FIN add 1 to that number + * -- For data packet, add the data length to that number + */ + +/* + * Data length requires a bit of fiddling around + */ + { + unsigned int data_len; + if( tcp->syn || tcp->fin ) + { + data_len = 1; /* Not real data, the SEQ clicks forward by 1 */ + } + else + { + data_len = ntohs( iph->tot_len ); + data_len -= 4 * iph->ihl; /* Subtract away IP header & options */ + data_len -= 4 * tcp->doff; /* Subtract away TCP header & options */ + } + + DEBUGP( KERN_WARNING "Data length calculated at %u\n", data_len ); + + if( data_len ) /* Only track events that demand an ACK */ + { + event.expected_ACK = ntohl( tcp->seq ) + data_len; + hash_insert( &connection, &event, direction ); + } + else + { + DEBUGP( "Don't bother to insert this, ACK not required\n" ); + } + } + + if( tcp->ack ) + { +/* + * Now we consider the matching of an existing event. + * Reverse the port numbers and change the ACK number to the actual ACK number + * Note that the direction is reversed because the reply will be going + * the opposite way to the request. + */ + event.expected_ACK = ntohl( tcp->ack_seq ); + event.dest_port = ntohs( tcp->source ); + event.source_port = ntohs( tcp->dest ); + request_complete( &connection, &event, !direction ); + } + else + { + DEBUGP( "Don't bother to check this, ACK not valid\n" ); + } + } + } + return( IPT_CONTINUE ); +} + +/* + * return( 0 ) if there is a problem with this entry (i.e. kick it out of the kernel) + * return( 1 ) if the entry is suitable + * + * tablename -- + * + * e -- + * + * targinfo -- Our private data block (handed to us from iptables plug-in) + * + * targinfosize -- The size of our private data block + * + * hook_mask -- + * + * + * Not much can go wrong for us, any illegal flags are harmlessly ignored, + * all possible flag combos make sense. All we check for is correct data size. + */ +static int checkentry( const char *tablename, + const struct ipt_entry *e, + void *targinfo, + unsigned int targinfosize, + unsigned int hook_mask ) +{ + const struct ipt_tcplag *el = targinfo; + + if( targinfosize != IPT_ALIGN( sizeof( struct ipt_tcplag ))) + { + DEBUGP( "TCPLAG: targinfosize %u != %u\n", targinfosize, + IPT_ALIGN( sizeof( struct ipt_tcplag ))); + return( 0 ); + } + if( el->prefix[ 14 ]) return( 0 ); /* Be sure to have terminated string */ + return( 1 ); +} + +static struct ipt_target reg = +{ + { 0, 0 }, + "TCPLAG", + &target, + &checkentry, + 0, + THIS_MODULE +}; + +static int __init init( void ) +{ + if( ipt_register_target( ® )) return( -EINVAL ); + hashsize = 123; /* should be configurable */ + hashtab = kmalloc( sizeof( void * ) * hashsize, GFP_ATOMIC ); + memset( hashtab, 0, sizeof( void * ) * hashsize ); + return( 0 ); +} + +/* + * This should not need locks (in theory) + * because it can only get punted after it is no longer + * chained into any of the netfilter lists. + */ +static void __exit fini( void ) +{ + int i; + + ipt_unregister_target( ® ); +/* + * Put back kernel memory + */ + for( i = 0; i < hashsize; i++ ) + { + tcplag_hash_T *p; + + if(( p = hashtab[ i ])) + { + tcplag_event_T *ev, *evn; + + hashtab[ i ] = 0; + for( ev = p->h_ACK_list; ev; ev = evn ) + { + evn = ev->next; + kfree( ev ); + } + for( ev = p->l_ACK_list; ev; ev = evn ) + { + evn = ev->next; + kfree( ev ); + } + kfree( p ); + } + } + kfree( hashtab ); +} + +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-16 09:16:25.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-16 09:16:27.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-16 09:15:41.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_osf.c linux-2.6.6-rc1/net/ipv4/netfilter/ipt_osf.c --- linux-2.6.6-rc1.org/net/ipv4/netfilter/ipt_osf.c 2004-04-16 08:59:08.000000000 +0200 +++ linux-2.6.6-rc1/net/ipv4/netfilter/ipt_osf.c 2004-04-16 09:20:53.000000000 +0200 @@ -44,6 +44,7 @@ #include #include #include +#include #include #include 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-16 09:18:14.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-16 09:18:18.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_rpc.c linux-2.6.6-rc1/net/ipv4/netfilter/ipt_rpc.c --- linux-2.6.6-rc1.org/net/ipv4/netfilter/ipt_rpc.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.6-rc1/net/ipv4/netfilter/ipt_rpc.c 2004-04-16 09:18:44.000000000 +0200 @@ -0,0 +1,428 @@ +/* RPC extension for IP connection matching, Version 2.2 + * (C) 2000 by Marcelo Barbosa Lima + * - original rpc tracking module + * - "recent" connection handling for kernel 2.3+ netfilter + * + * (C) 2001 by Rusty Russell + * - upgraded conntrack modules to oldnat api - kernel 2.4.0+ + * + * (C) 2002,2003 by Ian (Larry) Latter + * - upgraded conntrack modules to newnat api - kernel 2.4.20+ + * - extended matching to support filtering on procedures + * + * ipt_rpc.c,v 2.2 2003/01/12 18:30:00 + * + * 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. + ** + * Module load syntax: + * insmod ipt_rpc.o ports=port1,port2,...port + * + * Please give the ports of all RPC servers you wish to connect to. + * If you don't specify ports, the default will be port 111. + ** + * Note to all: + * + * RPCs should not be exposed to the internet - ask the Pentagon; + * + * "The unidentified crackers pleaded guilty in July to charges + * of juvenile delinquency stemming from a string of Pentagon + * network intrusions in February. + * + * The youths, going by the names TooShort and Makaveli, used + * a common server security hole to break in, according to + * Dane Jasper, owner of the California Internet service + * provider, Sonic. They used the hole, known as the 'statd' + * exploit, to attempt more than 800 break-ins, Jasper said." + * + * From: Wired News; "Pentagon Kids Kicked Off Grid" - Nov 6, 1998 + * URL: http://www.wired.com/news/politics/0,1283,16098,00.html + ** + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define MAX_PORTS 8 +static int ports[MAX_PORTS]; +static int ports_n_c = 0; + +#ifdef MODULE_PARM +MODULE_PARM(ports, "1-" __MODULE_STRING(MAX_PORTS) "i"); +MODULE_PARM_DESC(ports, "port numbers (TCP/UDP) of RPC portmapper servers"); +#endif + +MODULE_AUTHOR("Marcelo Barbosa Lima "); +MODULE_DESCRIPTION("RPC connection matching module"); +MODULE_LICENSE("GPL"); + +#if 0 +#define DEBUGP(format, args...) printk(KERN_DEBUG "ipt_rpc: " \ + format, ## args) +#else +#define DEBUGP(format, args...) +#endif + +EXPORT_NO_SYMBOLS; + +/* vars from ip_conntrack_rpc_tcp */ +extern struct list_head request_p_list_tcp; +extern struct module *ip_conntrack_rpc_tcp; + +/* vars from ip_conntrack_rpc_udp */ +extern struct list_head request_p_list_udp; +extern struct module *ip_conntrack_rpc_udp; + +DECLARE_RWLOCK_EXTERN(ipct_rpc_tcp_lock); +DECLARE_RWLOCK_EXTERN(ipct_rpc_udp_lock); + +#define ASSERT_READ_LOCK(x) \ +do { \ + if (x == &request_p_list_udp) \ + MUST_BE_READ_LOCKED(&ipct_rpc_udp_lock); \ + else if (x == &request_p_list_tcp) \ + MUST_BE_READ_LOCKED(&ipct_rpc_tcp_lock); \ +} while (0) + +#define ASSERT_WRITE_LOCK(x) \ +do { \ + if (x == &request_p_list_udp) \ + MUST_BE_WRITE_LOCKED(&ipct_rpc_udp_lock); \ + else if (x == &request_p_list_tcp) \ + MUST_BE_WRITE_LOCKED(&ipct_rpc_tcp_lock); \ +} while (0) + +#include + +const int IPT_RPC_CHAR_LEN = 11; + + +static int k_atoi(char *string) +{ + unsigned int result = 0; + int maxoctet = IPT_RPC_CHAR_LEN; + + for ( ; *string != 0 && maxoctet != 0; maxoctet--, string++) { + if (*string < 0) + return(0); + if (*string == 0) + break; + if (*string < 48 || *string > 57) { + return(0); + } + result = result * 10 + ( *string - 48 ); + } + return(result); +} + + +static int match_rpcs(char *c_procs, int i_procs, int proc) +{ + int proc_ctr; + char *proc_ptr; + unsigned int proc_num; + + DEBUGP("entered match_rpcs [%i] [%i] ..\n", i_procs, proc); + + if (i_procs == -1) + return 1; + + for (proc_ctr=0; proc_ctr <= i_procs; proc_ctr++) { + + proc_ptr = c_procs; + proc_ptr += proc_ctr * IPT_RPC_CHAR_LEN; + proc_num = k_atoi(proc_ptr); + + if (proc_num == proc) + return 1; + } + + return 0; +} + + +static int check_rpc_packet(const u_int32_t *data, const void *matchinfo, + int *hotdrop, int dir, struct ip_conntrack *ct, + int offset, struct list_head request_p_list) +{ + const struct ipt_rpc_info *rpcinfo = matchinfo; + struct request_p *req_p; + u_int32_t xid; + + + /* Get XID */ + xid = *data; + + /* This does sanity checking on RPC payloads, + * and permits only the RPC "get port" (3) + * in authorised procedures in client + * communications with the portmapper. + */ + + data += 5; + + /* Get RPC requestor */ + if (IXDR_GET_INT32(data) != 3) { + DEBUGP("RPC packet contains an invalid (non \"get\") requestor. [skip]\n"); + if(rpcinfo->strict == 1) + *hotdrop = 1; + return 0; + } + DEBUGP("RPC packet contains a \"get\" requestor. [cont]\n"); + + data++; + + /* Jump Credentials and Verfifier */ + data = data + IXDR_GET_INT32(data) + 2; + data = data + IXDR_GET_INT32(data) + 2; + + /* Get RPC procedure */ + if (match_rpcs((char *)&rpcinfo->c_procs, + rpcinfo->i_procs, IXDR_GET_INT32(data)) == 0) { + DEBUGP("RPC packet contains illegal procedure request [%u]. [drop]\n", + (unsigned int)IXDR_GET_INT32(data)); + + /* If the RPC conntrack half entry already exists .. */ + + switch (ct->tuplehash[0].tuple.dst.protonum) { + case IPPROTO_UDP: + WRITE_LOCK(&ipct_rpc_udp_lock); + case IPPROTO_TCP: + WRITE_LOCK(&ipct_rpc_tcp_lock); + } + req_p = LIST_FIND(&request_p_list, request_p_cmp, + struct request_p *, xid, + ct->tuplehash[dir].tuple.src.ip, + ct->tuplehash[dir].tuple.src.u.all); + + if (req_p) { + DEBUGP("found req_p for xid=%u proto=%u %u.%u.%u.%u:%u\n", + xid, ct->tuplehash[dir].tuple.dst.protonum, + NIPQUAD(ct->tuplehash[dir].tuple.src.ip), + ntohs(ct->tuplehash[dir].tuple.src.u.all)); + + /* .. remove it */ + if (del_timer(&req_p->timeout)) + req_p->timeout.expires = 0; + + LIST_DELETE(&request_p_list, req_p); + DEBUGP("RPC req_p removed. [done]\n"); + + } else { + DEBUGP("no req_p found for xid=%u proto=%u %u.%u.%u.%u:%u\n", + xid, ct->tuplehash[dir].tuple.dst.protonum, + NIPQUAD(ct->tuplehash[dir].tuple.src.ip), + ntohs(ct->tuplehash[dir].tuple.src.u.all)); + + } + switch (ct->tuplehash[0].tuple.dst.protonum) { + case IPPROTO_UDP: + WRITE_UNLOCK(&ipct_rpc_udp_lock); + case IPPROTO_TCP: + WRITE_UNLOCK(&ipct_rpc_tcp_lock); + } + + if(rpcinfo->strict == 1) + *hotdrop = 1; + return 0; + } + + DEBUGP("RPC packet contains authorised procedure request [%u]. [match]\n", + (unsigned int)IXDR_GET_INT32(data)); + return (1 && (!offset)); +} + + +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) +{ + struct ip_conntrack *ct; + enum ip_conntrack_info ctinfo; + const u_int32_t *data; + enum ip_conntrack_dir dir; + const struct tcphdr *tcp; + const struct ipt_rpc_info *rpcinfo = matchinfo; + int port, portsok; + int tval; + + + DEBUGP("new packet to evaluate ..\n"); + + ct = ip_conntrack_get((struct sk_buff *)skb, &ctinfo); + if (!ct) { + DEBUGP("no ct available [skip]\n"); + return 0; + } + + DEBUGP("ct detected. [cont]\n"); + dir = CTINFO2DIR(ctinfo); + + /* we only want the client to server packets for matching */ + if (dir != IP_CT_DIR_ORIGINAL) + return 0; + + /* This does sanity checking on UDP or TCP packets, + * like their respective modules. + */ + + switch (ct->tuplehash[0].tuple.dst.protonum) { + + case IPPROTO_UDP: + DEBUGP("PROTO_UDP [cont]\n"); + if (offset == 0 && datalen < sizeof(struct udphdr)) { + DEBUGP("packet does not contain a complete header. [drop]\n"); + return 0; + } + + for (port=0,portsok=0; port <= ports_n_c; port++) { + if (ntohs(ct->tuplehash[dir].tuple.dst.u.all) == ports[port]) { + portsok++; + break; + } + } + if (portsok == 0) { + DEBUGP("packet is not destined for a portmapper [%u]. [skip]\n", + ntohs(ct->tuplehash[dir].tuple.dst.u.all)); + return 0; + } + + if ((datalen - sizeof(struct udphdr)) != 56) { + DEBUGP("packet length is not correct for RPC content. [skip]\n"); + if (rpcinfo->strict == 1) + *hotdrop = 1; + return 0; + } + DEBUGP("packet length is correct. [cont]\n"); + + /* Get to the data */ + data = (const u_int32_t *)hdr + 2; + + /* Check the RPC data */ + tval = check_rpc_packet(data, matchinfo, hotdrop, + dir, ct, offset, + request_p_list_udp); + + return tval; + + + case IPPROTO_TCP: + DEBUGP("PROTO_TCP [cont]\n"); + if (offset == 0 && datalen < sizeof(struct tcphdr)) { + DEBUGP("packet does not contain a complete header. [drop]\n"); + return 0; + } + + for (port=0,portsok=0; port <= ports_n_c; port++) { + if (ntohs(ct->tuplehash[dir].tuple.dst.u.all) == ports[port]) { + portsok++; + break; + } + } + if (portsok == 0) { + DEBUGP("packet is not destined for a portmapper [%u]. [skip]\n", + ntohs(ct->tuplehash[dir].tuple.dst.u.all)); + return 0; + } + + tcp = hdr; + if (datalen == (tcp->doff * 4)) { + DEBUGP("packet does not contain any data. [match]\n"); + return (1 && (!offset)); + } + + /* Tests if packet len is ok */ + if ((datalen - (tcp->doff * 4)) != 60) { + DEBUGP("packet length is not correct for RPC content. [skip]\n"); + if(rpcinfo->strict == 1) + *hotdrop = 1; + return 0; + } + DEBUGP("packet length is correct. [cont]\n"); + + /* Get to the data */ + data = (const u_int32_t *)tcp + tcp->doff + 1; + + /* Check the RPC data */ + tval = check_rpc_packet(data, matchinfo, hotdrop, + dir, ct, offset, + request_p_list_tcp); + + return tval; + + } + + DEBUGP("transport protocol=%u, is not supported [skip]\n", + ct->tuplehash[0].tuple.dst.protonum); + return 0; +} + + +static int checkentry(const char *tablename, const struct ipt_ip *ip, void *matchinfo, + unsigned int matchsize, unsigned int hook_mask) +{ + if (hook_mask + & ~((1 << NF_IP_PRE_ROUTING) | (1 << NF_IP_FORWARD) | (1 << NF_IP_POST_ROUTING) + | (1 << NF_IP_LOCAL_IN) | (1 << NF_IP_LOCAL_OUT))) { + printk("ipt_rpc: only valid for PRE_ROUTING, FORWARD, POST_ROUTING, LOCAL_IN and/or LOCAL_OUT targets.\n"); + return 0; + } + + if (matchsize != IPT_ALIGN(sizeof(struct ipt_rpc_info))) + return 0; + + return 1; +} + + +static struct ipt_match rpc_match = { { NULL, NULL }, "rpc", + &match, &checkentry, NULL, + THIS_MODULE }; + + +static int __init init(void) +{ + int port; + + DEBUGP("incrementing usage counts\n"); + __MOD_INC_USE_COUNT(ip_conntrack_rpc_udp); + __MOD_INC_USE_COUNT(ip_conntrack_rpc_tcp); + + /* If no port given, default to standard RPC port */ + if (ports[0] == 0) + ports[0] = RPC_PORT; + + DEBUGP("registering match [%s] for;\n", rpc_match.name); + for (port = 0; (port < MAX_PORTS) && ports[port]; port++) { + DEBUGP(" port %i (UDP|TCP);\n", ports[port]); + ports_n_c++; + } + + return ipt_register_match(&rpc_match); +} + + +static void fini(void) +{ + DEBUGP("unregistering match\n"); + ipt_unregister_match(&rpc_match); + + DEBUGP("decrementing usage counts\n"); + __MOD_DEC_USE_COUNT(ip_conntrack_rpc_tcp); + __MOD_DEC_USE_COUNT(ip_conntrack_rpc_udp); +} + + +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-16 09:19:37.000000000 +0200 @@ -0,0 +1,218 @@ +/* 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 + +MODULE_LICENSE("GPL"); + +struct string_per_cpu { + int *skip; + int *shift; + int *len; +}; + +struct string_per_cpu *bm_string_data=NULL; + +/* 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=bm_string_data[smp_processor_id()].shift; + skip=bm_string_data[smp_processor_id()].skip; + len=bm_string_data[smp_processor_id()].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[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[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, + const void *hdr, + u_int16_t datalen, + 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; +} + +void string_freeup_data(void) +{ + int c; + + if ( bm_string_data ) { + for(c=0; c +#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/raw.c linux-2.6.6-rc1/net/ipv4/raw.c --- linux-2.6.6-rc1.org/net/ipv4/raw.c 2004-04-15 03:35:04.000000000 +0200 +++ linux-2.6.6-rc1/net/ipv4/raw.c 2004-04-16 09:17:13.000000000 +0200 @@ -249,6 +249,7 @@ kfree_skb(skb); return NET_RX_DROP; } + nf_reset(skb); skb_push(skb, skb->data - skb->nh.raw); @@ -307,7 +308,7 @@ } err = NF_HOOK(PF_INET, NF_IP_LOCAL_OUT, skb, NULL, rt->u.dst.dev, - dst_output); + ip_dst_output); if (err > 0) err = inet->recverr ? net_xmit_errno(err) : 0; if (err) 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-16 09:18:14.000000000 +0200 @@ -1785,6 +1785,7 @@ if (!xfrm4_policy_check(sk, XFRM_POLICY_IN, skb)) goto discard_and_relse; + nf_reset(skb); if (sk_filter(sk, skb, 0)) goto discard_and_relse; @@ -2670,6 +2671,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-15 03:34:02.000000000 +0200 +++ linux-2.6.6-rc1/net/ipv4/udp.c 2004-04-16 09:18:14.000000000 +0200 @@ -1030,6 +1030,7 @@ kfree_skb(skb); return -1; } + nf_reset(skb); if (up->encap_type) { /* @@ -1195,6 +1196,7 @@ if (!xfrm4_policy_check(NULL, XFRM_POLICY_IN, skb)) goto drop; + nf_reset(skb); /* No socket. Drop packet silently, if checksum is wrong */ if (udp_checksum_complete(skb)) @@ -1543,6 +1545,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); diff -Nur --exclude '*.orig' linux-2.6.6-rc1.org/net/ipv4/xfrm4_tunnel.c linux-2.6.6-rc1/net/ipv4/xfrm4_tunnel.c --- linux-2.6.6-rc1.org/net/ipv4/xfrm4_tunnel.c 2004-04-15 03:33:55.000000000 +0200 +++ linux-2.6.6-rc1/net/ipv4/xfrm4_tunnel.c 2004-04-16 09:17:09.000000000 +0200 @@ -76,6 +76,7 @@ err = -EHOSTUNREACH; goto error_nolock; } + IPCB(skb)->flags |= IPSKB_XFRM_TRANSFORMED; return NET_XMIT_BYPASS; error_nolock: @@ -170,6 +171,7 @@ .handler = ipip_rcv, .err_handler = ipip_err, .no_policy = 1, + .xfrm_prot = 1, }; static int __init ipip_init(void) diff -Nur --exclude '*.orig' linux-2.6.6-rc1.org/net/ipv6/netfilter/Kconfig linux-2.6.6-rc1/net/ipv6/netfilter/Kconfig --- linux-2.6.6-rc1.org/net/ipv6/netfilter/Kconfig 2004-04-16 08:59:09.000000000 +0200 +++ linux-2.6.6-rc1/net/ipv6/netfilter/Kconfig 2004-04-16 09:15:44.000000000 +0200 @@ -255,5 +255,10 @@ . If unsure, say `N'. help +config IP6_NF_TARGET_ROUTE + tristate ' ROUTE target support' + depends on IP6_NF_MANGLE + help + endmenu diff -Nur --exclude '*.orig' linux-2.6.6-rc1.org/net/ipv6/netfilter/Makefile linux-2.6.6-rc1/net/ipv6/netfilter/Makefile --- linux-2.6.6-rc1.org/net/ipv6/netfilter/Makefile 2004-04-16 08:59:09.000000000 +0200 +++ linux-2.6.6-rc1/net/ipv6/netfilter/Makefile 2004-04-16 09:15:44.000000000 +0200 @@ -20,6 +20,7 @@ obj-$(CONFIG_IP6_NF_FILTER) += ip6table_filter.o obj-$(CONFIG_IP6_NF_MANGLE) += ip6table_mangle.o obj-$(CONFIG_IP6_NF_TARGET_MARK) += ip6t_MARK.o +obj-$(CONFIG_IP6_NF_TARGET_ROUTE) += ip6t_ROUTE.o obj-$(CONFIG_IP6_NF_TARGET_REJECT) += ip6t_REJECT.o obj-$(CONFIG_IP6_NF_QUEUE) += ip6_queue.o obj-$(CONFIG_IP6_NF_TARGET_LOG) += ip6t_LOG.o diff -Nur --exclude '*.orig' linux-2.6.6-rc1.org/net/ipv6/netfilter/ip6t_ROUTE.c linux-2.6.6-rc1/net/ipv6/netfilter/ip6t_ROUTE.c --- linux-2.6.6-rc1.org/net/ipv6/netfilter/ip6t_ROUTE.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.6-rc1/net/ipv6/netfilter/ip6t_ROUTE.c 2004-04-16 09:15:44.000000000 +0200 @@ -0,0 +1,289 @@ +/* + * This implements the ROUTE v6 target, which enables you to setup unusual + * routes not supported by the standard kernel routing table. + * + * Copyright (C) 2003 Cedric de Launois + * + * v 1.0 2003/08/05 + * + * This software is distributed under GNU GPL v2, 1991 + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if 1 +#define DEBUGP printk +#else +#define DEBUGP(format, args...) +#endif + +#define NIP6(addr) \ + ntohs((addr).s6_addr16[0]), \ + ntohs((addr).s6_addr16[1]), \ + ntohs((addr).s6_addr16[2]), \ + ntohs((addr).s6_addr16[3]), \ + ntohs((addr).s6_addr16[4]), \ + ntohs((addr).s6_addr16[5]), \ + ntohs((addr).s6_addr16[6]), \ + ntohs((addr).s6_addr16[7]) + +/* Route the packet according to the routing keys specified in + * route_info. Keys are : + * - ifindex : + * 0 if no oif preferred, + * otherwise set to the index of the desired oif + * - route_info->gw : + * 0 if no gateway specified, + * otherwise set to the next host to which the pkt must be routed + * If success, skb->dev is the output device to which the packet must + * be sent and skb->dst is not NULL + * + * RETURN: 1 if the packet was succesfully routed to the + * destination desired + * 0 if the kernel routing table could not route the packet + * according to the keys specified + */ +static int +route6(struct sk_buff *skb, + unsigned int ifindex, + const struct ip6t_route_target_info *route_info) +{ + struct rt6_info *rt = NULL; + struct ipv6hdr *ipv6h = skb->nh.ipv6h; + struct in6_addr *gw = (struct in6_addr*)&route_info->gw; + + DEBUGP("ip6t_ROUTE: called with: "); + DEBUGP("DST=%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x ", NIP6(ipv6h->daddr)); + DEBUGP("GATEWAY=%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x ", NIP6(*gw)); + DEBUGP("OUT=%s\n", route_info->oif); + + if (ipv6_addr_any(gw)) + rt = rt6_lookup(&ipv6h->daddr, &ipv6h->saddr, ifindex, 1); + else + rt = rt6_lookup(gw, &ipv6h->saddr, ifindex, 1); + + if (!rt) + goto no_route; + + DEBUGP("ip6t_ROUTE: routing gives: "); + DEBUGP("DST=%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x ", NIP6(rt->rt6i_dst.addr)); + DEBUGP("GATEWAY=%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x ", NIP6(rt->rt6i_gateway)); + DEBUGP("OUT=%s\n", rt->rt6i_dev->name); + + if (ifindex && rt->rt6i_dev->ifindex!=ifindex) + goto wrong_route; + + if (!rt->rt6i_nexthop) { + DEBUGP("ip6t_ROUTE: discovering neighbour\n"); + rt->rt6i_nexthop = ndisc_get_neigh(rt->rt6i_dev, &rt->rt6i_dst.addr); + } + + /* Drop old route. */ + dst_release(skb->dst); + skb->dst = &rt->u.dst; + skb->dev = rt->rt6i_dev; + return 1; + + wrong_route: + dst_release(&rt->u.dst); + no_route: + if (!net_ratelimit()) + return 0; + + printk("ip6t_ROUTE: no explicit route found "); + if (ifindex) + printk("via interface %s ", route_info->oif); + if (!ipv6_addr_any(gw)) + printk("via gateway %04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x", NIP6(*gw)); + printk("\n"); + return 0; +} + + +/* Stolen from ip6_output_finish + * PRE : skb->dev is set to the device we are leaving by + * skb->dst is not NULL + * POST: the packet is sent with the link layer header pushed + * the packet is destroyed + */ +static void 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); + hh->hh_output(skb); + } else if (dst->neighbour) + dst->neighbour->output(skb); + else { + if (net_ratelimit()) + DEBUGP(KERN_DEBUG "ip6t_ROUTE: no hdr & no neighbour cache!\n"); + kfree_skb(skb); + } +} + + +static unsigned int +route6_oif(const struct ip6t_route_target_info *route_info, + struct sk_buff *skb) +{ + unsigned int ifindex = 0; + struct net_device *dev_out = NULL; + + /* The user set the interface name to use. + * Getting the current interface index. + */ + if ((dev_out = dev_get_by_name(route_info->oif))) { + ifindex = dev_out->ifindex; + } else { + /* Unknown interface name : packet dropped */ + if (net_ratelimit()) + DEBUGP("ip6t_ROUTE: oif interface %s not found\n", route_info->oif); + + if (route_info->flags & IP6T_ROUTE_CONTINUE) + return IP6T_CONTINUE; + else + return NF_DROP; + } + + /* Trying the standard way of routing packets */ + if (route6(skb, ifindex, route_info)) { + dev_put(dev_out); + if (route_info->flags & IP6T_ROUTE_CONTINUE) + return IP6T_CONTINUE; + + ip_direct_send(skb); + return NF_STOLEN; + } else + return NF_DROP; +} + + +static unsigned int +route6_gw(const struct ip6t_route_target_info *route_info, + struct sk_buff *skb) +{ + if (route6(skb, 0, route_info)) { + if (route_info->flags & IP6T_ROUTE_CONTINUE) + return IP6T_CONTINUE; + + ip_direct_send(skb); + return NF_STOLEN; + } else + return NF_DROP; +} + + +static unsigned int +ip6t_route_target(struct sk_buff **pskb, + unsigned int hooknum, + const struct net_device *in, + const struct net_device *out, + const void *targinfo, + void *userinfo) +{ + const struct ip6t_route_target_info *route_info = targinfo; + struct sk_buff *skb = *pskb; + struct in6_addr *gw = (struct in6_addr*)&route_info->gw; + + if (route_info->flags & IP6T_ROUTE_CONTINUE) + goto do_it; + + /* If we are at PREROUTING or INPUT hook + * the TTL isn't decreased by the IP stack + */ + if (hooknum == NF_IP6_PRE_ROUTING || + hooknum == NF_IP6_LOCAL_IN) { + + struct ipv6hdr *ipv6h = skb->nh.ipv6h; + + if (ipv6h->hop_limit <= 1) { + /* Force OUTPUT device used as source address */ + skb->dev = skb->dst->dev; + + icmpv6_send(skb, ICMPV6_TIME_EXCEED, + ICMPV6_EXC_HOPLIMIT, 0, skb->dev); + + return NF_DROP; + } + + ipv6h->hop_limit--; + } + + + do_it: + if (route_info->oif[0]) + return route6_oif(route_info, *pskb); + + if (!ipv6_addr_any(gw)) + return route6_gw(route_info, *pskb); + + if (net_ratelimit()) + DEBUGP(KERN_DEBUG "ip6t_ROUTE: no parameter !\n"); + + return IP6T_CONTINUE; +} + + +static int +ip6t_route_checkentry(const char *tablename, + const struct ip6t_entry *e, + void *targinfo, + unsigned int targinfosize, + unsigned int hook_mask) +{ + if (strcmp(tablename, "mangle") != 0) { + printk("ip6t_ROUTE: can only be called from \"mangle\" table.\n"); + return 0; + } + + if (targinfosize != IP6T_ALIGN(sizeof(struct ip6t_route_target_info))) { + printk(KERN_WARNING "ip6t_ROUTE: targinfosize %u != %Zu\n", + targinfosize, + IP6T_ALIGN(sizeof(struct ip6t_route_target_info))); + return 0; + } + + return 1; +} + + +static struct ip6t_target ip6t_route_reg = { + .name = "ROUTE", + .target = ip6t_route_target, + .checkentry = ip6t_route_checkentry, + .me = THIS_MODULE +}; + + +static int __init init(void) +{ + printk(KERN_DEBUG "registering ipv6 ROUTE target\n"); + if (ip6t_register_target(&ip6t_route_reg)) + return -EINVAL; + + return 0; +} + + +static void __exit fini(void) +{ + ip6t_unregister_target(&ip6t_route_reg); +} + +module_init(init); +module_exit(fini); +MODULE_LICENSE("GPL"); diff -Nur --exclude '*.orig' linux-2.6.6-rc1.org/net/ipv6/netfilter/ip6t_owner.c linux-2.6.6-rc1/net/ipv6/netfilter/ip6t_owner.c --- linux-2.6.6-rc1.org/net/ipv6/netfilter/ip6t_owner.c 2004-04-15 03:35:20.000000000 +0200 +++ linux-2.6.6-rc1/net/ipv6/netfilter/ip6t_owner.c 2004-04-16 09:18:17.000000000 +0200 @@ -21,6 +21,38 @@ MODULE_LICENSE("GPL"); static int +match_comm(const struct sk_buff *skb, const char *comm) +{ + struct task_struct *p; + struct files_struct *files; + int i; + + read_lock(&tasklist_lock); + for_each_task(p) { + if(strncmp(p->comm, comm, sizeof(p->comm))) + continue; + + task_lock(p); + files = p->files; + if(files) { + read_lock(&files->file_lock); + for (i=0; i < files->max_fds; i++) { + if (fcheck_files(files, i) == skb->sk->socket->file) { + read_unlock(&files->file_lock); + task_unlock(p); + read_unlock(&tasklist_lock); + return 1; + } + } + read_unlock(&files->file_lock); + } + task_unlock(p); + } + read_unlock(&tasklist_lock); + return 0; +} + +static int match_pid(const struct sk_buff *skb, pid_t pid) { struct task_struct *p; @@ -125,6 +157,12 @@ return 0; } + if(info->match & IP6T_OWNER_COMM) { + if (!match_comm(skb, info->comm) ^ + !!(info->invert & IP6T_OWNER_COMM)) + return 0; + } + return 1; } diff -Nur --exclude '*.orig' linux-2.6.6-rc1.org/net/xfrm/xfrm_input.c linux-2.6.6-rc1/net/xfrm/xfrm_input.c --- linux-2.6.6-rc1.org/net/xfrm/xfrm_input.c 2004-04-15 03:33:51.000000000 +0200 +++ linux-2.6.6-rc1/net/xfrm/xfrm_input.c 2004-04-16 09:17:09.000000000 +0200 @@ -29,6 +29,9 @@ if (!sp) return NULL; +#ifdef CONFIG_NETFILTER + sp->decap_done = 0; +#endif sp->len = 0; if (src) { int i; diff -Nur --exclude '*.orig' linux-2.6.6-rc1.org/net/xfrm/xfrm_policy.c linux-2.6.6-rc1/net/xfrm/xfrm_policy.c --- linux-2.6.6-rc1.org/net/xfrm/xfrm_policy.c 2004-04-15 03:34:48.000000000 +0200 +++ linux-2.6.6-rc1/net/xfrm/xfrm_policy.c 2004-04-16 09:17:13.000000000 +0200 @@ -21,6 +21,7 @@ #include #include #include +#include #include #include @@ -908,6 +909,7 @@ if (_decode_session(skb, &fl, family) < 0) return 0; + nf_nat_decode_session(skb, &fl, family); /* First, check used SA against their selectors. */ if (skb->sp) {