diff -Nur --exclude '*.orig' linux-2.6.6-rc3.org/include/linux/netfilter.h linux-2.6.6-rc3/include/linux/netfilter.h --- linux-2.6.6-rc3.org/include/linux/netfilter.h 2004-04-28 03:35:48.000000000 +0200 +++ linux-2.6.6-rc3/include/linux/netfilter.h 2004-04-29 11:18:06.000000000 +0200 @@ -23,6 +23,7 @@ <= 0x2000 is used for protocol-flags. */ #define NFC_UNKNOWN 0x4000 #define NFC_ALTERED 0x8000 +#define NFC_TRACE 0x10000 #ifdef __KERNEL__ #include @@ -137,12 +138,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 +153,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 +187,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-rc3.org/include/linux/netfilter_helpers.h linux-2.6.6-rc3/include/linux/netfilter_helpers.h --- linux-2.6.6-rc3.org/include/linux/netfilter_helpers.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.6-rc3/include/linux/netfilter_helpers.h 2004-04-29 11:20:13.000000000 +0200 @@ -0,0 +1,133 @@ +/* + * Helpers for netfiler modules. This file provides implementations for basic + * functions such as strncasecmp(), etc. + * + * gcc will warn for defined but unused functions, so we only include the + * functions requested. The following macros are used: + * NF_NEED_STRNCASECMP nf_strncasecmp() + * NF_NEED_STRTOU16 nf_strtou16() + * NF_NEED_STRTOU32 nf_strtou32() + */ +#ifndef _NETFILTER_HELPERS_H +#define _NETFILTER_HELPERS_H + +/* Only include these functions for kernel code. */ +#ifdef __KERNEL__ + +#include +#define iseol(c) ( (c) == '\r' || (c) == '\n' ) + +/* + * The standard strncasecmp() + */ +#ifdef NF_NEED_STRNCASECMP +static int +nf_strncasecmp(const char* s1, const char* s2, u_int32_t len) +{ + if (s1 == NULL || s2 == NULL) + { + if (s1 == NULL && s2 == NULL) + { + return 0; + } + return (s1 == NULL) ? -1 : 1; + } + while (len > 0 && tolower(*s1) == tolower(*s2)) + { + len--; + s1++; + s2++; + } + return ( (len == 0) ? 0 : (tolower(*s1) - tolower(*s2)) ); +} +#endif /* NF_NEED_STRNCASECMP */ + +/* + * Parse a string containing a 16-bit unsigned integer. + * Returns the number of chars used, or zero if no number is found. + */ +#ifdef NF_NEED_STRTOU16 +static int +nf_strtou16(const char* pbuf, u_int16_t* pval) +{ + int n = 0; + + *pval = 0; + while (isdigit(pbuf[n])) + { + *pval = (*pval * 10) + (pbuf[n] - '0'); + n++; + } + + return n; +} +#endif /* NF_NEED_STRTOU16 */ + +/* + * Parse a string containing a 32-bit unsigned integer. + * Returns the number of chars used, or zero if no number is found. + */ +#ifdef NF_NEED_STRTOU32 +static int +nf_strtou32(const char* pbuf, u_int32_t* pval) +{ + int n = 0; + + *pval = 0; + while (pbuf[n] >= '0' && pbuf[n] <= '9') + { + *pval = (*pval * 10) + (pbuf[n] - '0'); + n++; + } + + return n; +} +#endif /* NF_NEED_STRTOU32 */ + +/* + * Given a buffer and length, advance to the next line and mark the current + * line. + */ +#ifdef NF_NEED_NEXTLINE +static int +nf_nextline(char* p, uint len, uint* poff, uint* plineoff, uint* plinelen) +{ + uint off = *poff; + uint physlen = 0; + + if (off >= len) + { + return 0; + } + + while (p[off] != '\n') + { + if (len-off <= 1) + { + return 0; + } + + physlen++; + off++; + } + + /* if we saw a crlf, physlen needs adjusted */ + if (physlen > 0 && p[off] == '\n' && p[off-1] == '\r') + { + physlen--; + } + + /* advance past the newline */ + off++; + + *plineoff = *poff; + *plinelen = physlen; + *poff = off; + + return 1; +} +#endif /* NF_NEED_NEXTLINE */ + +#endif /* __KERNEL__ */ + +#endif /* _NETFILTER_HELPERS_H */ diff -Nur --exclude '*.orig' linux-2.6.6-rc3.org/include/linux/netfilter_ipv4/ip_conntrack.h linux-2.6.6-rc3/include/linux/netfilter_ipv4/ip_conntrack.h --- linux-2.6.6-rc3.org/include/linux/netfilter_ipv4/ip_conntrack.h 2004-04-28 03:36:37.000000000 +0200 +++ linux-2.6.6-rc3/include/linux/netfilter_ipv4/ip_conntrack.h 2004-04-29 11:21:43.000000000 +0200 @@ -51,10 +51,12 @@ #include #include +#include /* per conntrack: protocol private data */ union ip_conntrack_proto { /* insert conntrack proto private data here */ + struct ip_ct_sctp sctp; struct ip_ct_tcp tcp; struct ip_ct_icmp icmp; }; @@ -64,6 +66,11 @@ }; /* Add protocol helper include file here */ +#include +#include +#include +#include +#include #include #include #include @@ -71,6 +78,11 @@ /* 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_rtsp_expect exp_rtsp_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 +97,11 @@ /* 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_rtsp_master ct_rtsp_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 +224,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-rc3.org/include/linux/netfilter_ipv4/ip_conntrack_cuseeme.h linux-2.6.6-rc3/include/linux/netfilter_ipv4/ip_conntrack_cuseeme.h --- linux-2.6.6-rc3.org/include/linux/netfilter_ipv4/ip_conntrack_cuseeme.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.6-rc3/include/linux/netfilter_ipv4/ip_conntrack_cuseeme.h 2004-04-29 11:17:55.000000000 +0200 @@ -0,0 +1,70 @@ +#ifndef _IP_CT_CUSEEME +#define _IP_CT_CUSEEME + +#define CUSEEME_PORT 7648 + +/* These structs come from the 2.2 ip_masq_cuseeme code... */ + +#pragma pack(1) +/* CuSeeMe data header */ +struct cu_header { + u_int16_t dest_family; + u_int16_t dest_port; + u_int32_t dest_addr; + int16_t family; + u_int16_t port; + u_int32_t addr; + u_int32_t seq; + u_int16_t msg; + u_int16_t data_type; + /* possible values: + * 1 small video + * 2 big video + * 3 audio + * 100 acknowledge connectivity when there + * is nothing else to send + * 101 OpenContinue packet + * 104 display a text message and + * disconnect (used by reflector to + * kick clients off) + * 105 display a text message (welcome + * message from reflector) + * 106 exchanged among reflectors for + * reflector interoperation + * 107 carry aux stream data when there is + * no video to piggy-back on + * 108 obsolete (used in Mac alpha version) + * 109 obsolete (used in Mac alpha version) + * 110 used for data rate control + * 111 used for data rate control + * 256 aux data control messages + * 257 aux data packets + * */ + u_int16_t packet_len; +}; + +/* Open Continue Header */ +struct oc_header { + struct cu_header cu_head; + u_int16_t client_count; /* Number of client info structs */ + u_int32_t seq_no; + char user_name[20]; + char stuff[4]; /* Flags, version stuff, etc */ +}; + +/* Client info structures */ +struct client_info { + u_int32_t address; /* Client address */ + char stuff[8]; /* Flags, pruning bitfield, packet counts, etc */ +}; +#pragma pack() + +/* This structure is per expected connection */ +struct ip_ct_cuseeme_expect { +}; + +/* This structure exists only once per master */ +struct ip_ct_cuseeme_master { +}; + +#endif /* _IP_CT_CUSEEME */ diff -Nur --exclude '*.orig' linux-2.6.6-rc3.org/include/linux/netfilter_ipv4/ip_conntrack_h323.h linux-2.6.6-rc3/include/linux/netfilter_ipv4/ip_conntrack_h323.h --- linux-2.6.6-rc3.org/include/linux/netfilter_ipv4/ip_conntrack_h323.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.6-rc3/include/linux/netfilter_ipv4/ip_conntrack_h323.h 2004-04-29 11:18:00.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-rc3.org/include/linux/netfilter_ipv4/ip_conntrack_mms.h linux-2.6.6-rc3/include/linux/netfilter_ipv4/ip_conntrack_mms.h --- linux-2.6.6-rc3.org/include/linux/netfilter_ipv4/ip_conntrack_mms.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.6-rc3/include/linux/netfilter_ipv4/ip_conntrack_mms.h 2004-04-29 11:19:01.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-rc3.org/include/linux/netfilter_ipv4/ip_conntrack_quake3.h linux-2.6.6-rc3/include/linux/netfilter_ipv4/ip_conntrack_quake3.h --- linux-2.6.6-rc3.org/include/linux/netfilter_ipv4/ip_conntrack_quake3.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.6-rc3/include/linux/netfilter_ipv4/ip_conntrack_quake3.h 2004-04-29 11:19:53.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-rc3.org/include/linux/netfilter_ipv4/ip_conntrack_rpc.h linux-2.6.6-rc3/include/linux/netfilter_ipv4/ip_conntrack_rpc.h --- linux-2.6.6-rc3.org/include/linux/netfilter_ipv4/ip_conntrack_rpc.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.6-rc3/include/linux/netfilter_ipv4/ip_conntrack_rpc.h 2004-04-29 11:20:02.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-rc3.org/include/linux/netfilter_ipv4/ip_conntrack_rsh.h linux-2.6.6-rc3/include/linux/netfilter_ipv4/ip_conntrack_rsh.h --- linux-2.6.6-rc3.org/include/linux/netfilter_ipv4/ip_conntrack_rsh.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.6-rc3/include/linux/netfilter_ipv4/ip_conntrack_rsh.h 2004-04-29 11:20:06.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-rc3.org/include/linux/netfilter_ipv4/ip_conntrack_rtsp.h linux-2.6.6-rc3/include/linux/netfilter_ipv4/ip_conntrack_rtsp.h --- linux-2.6.6-rc3.org/include/linux/netfilter_ipv4/ip_conntrack_rtsp.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.6-rc3/include/linux/netfilter_ipv4/ip_conntrack_rtsp.h 2004-04-29 11:20:13.000000000 +0200 @@ -0,0 +1,68 @@ +/* + * RTSP extension for IP connection tracking. + * (C) 2003 by Tom Marshall + * based on ip_conntrack_irc.h + * + * 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_RTSP_H +#define _IP_CONNTRACK_RTSP_H + +/* #define IP_NF_RTSP_DEBUG */ +#define IP_NF_RTSP_VERSION "0.01" + +/* port block types */ +typedef enum { + pb_single, /* client_port=x */ + pb_range, /* client_port=x-y */ + pb_discon /* client_port=x/y (rtspbis) */ +} portblock_t; + +/* We record seq number and length of rtsp headers here, all in host order. */ + +/* + * This structure is per expected connection. It is a member of struct + * ip_conntrack_expect. The TCP SEQ for the conntrack expect is stored + * there and we are expected to only store the length of the data which + * needs replaced. If a packet contains multiple RTSP messages, we create + * one expected connection per message. + * + * We use these variables to mark the entire header block. This may seem + * like overkill, but the nature of RTSP requires it. A header may appear + * multiple times in a message. We must treat two Transport headers the + * same as one Transport header with two entries. + */ +struct ip_ct_rtsp_expect +{ + u_int32_t len; /* length of header block */ + portblock_t pbtype; /* Type of port block that was requested */ + u_int16_t loport; /* Port that was requested, low or first */ + u_int16_t hiport; /* Port that was requested, high or second */ +#if 0 + uint method; /* RTSP method */ + uint cseq; /* CSeq from request */ +#endif +}; + +/* This structure exists only once per master */ +struct ip_ct_rtsp_master +{ + /* Empty (?) */ +}; + + +#ifdef __KERNEL__ + +#include + +#define RTSP_PORT 554 + +/* Protects rtsp part of conntracks */ +DECLARE_LOCK_EXTERN(ip_rtsp_lock); + +#endif /* __KERNEL__ */ + +#endif /* _IP_CONNTRACK_RTSP_H */ diff -Nur --exclude '*.orig' linux-2.6.6-rc3.org/include/linux/netfilter_ipv4/ip_conntrack_sctp.h linux-2.6.6-rc3/include/linux/netfilter_ipv4/ip_conntrack_sctp.h --- linux-2.6.6-rc3.org/include/linux/netfilter_ipv4/ip_conntrack_sctp.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.6-rc3/include/linux/netfilter_ipv4/ip_conntrack_sctp.h 2004-04-29 11:20:17.000000000 +0200 @@ -0,0 +1,25 @@ +#ifndef _IP_CONNTRACK_SCTP_H +#define _IP_CONNTRACK_SCTP_H +/* SCTP tracking. */ + +enum sctp_conntrack { + SCTP_CONNTRACK_NONE, + SCTP_CONNTRACK_CLOSED, + SCTP_CONNTRACK_COOKIE_WAIT, + SCTP_CONNTRACK_COOKIE_ECHOED, + SCTP_CONNTRACK_ESTABLISHED, + SCTP_CONNTRACK_SHUTDOWN_SENT, + SCTP_CONNTRACK_SHUTDOWN_RECD, + SCTP_CONNTRACK_SHUTDOWN_ACK_SENT, + SCTP_CONNTRACK_MAX +}; + +struct ip_ct_sctp +{ + enum sctp_conntrack state; + + u_int32_t vtag[IP_CT_DIR_MAX]; + u_int32_t ttag[IP_CT_DIR_MAX]; +}; + +#endif /* _IP_CONNTRACK_SCTP_H */ diff -Nur --exclude '*.orig' linux-2.6.6-rc3.org/include/linux/netfilter_ipv4/ip_conntrack_talk.h linux-2.6.6-rc3/include/linux/netfilter_ipv4/ip_conntrack_talk.h --- linux-2.6.6-rc3.org/include/linux/netfilter_ipv4/ip_conntrack_talk.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.6-rc3/include/linux/netfilter_ipv4/ip_conntrack_talk.h 2004-04-29 11:21:43.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-rc3.org/include/linux/netfilter_ipv4/ip_conntrack_tuple.h linux-2.6.6-rc3/include/linux/netfilter_ipv4/ip_conntrack_tuple.h --- linux-2.6.6-rc3.org/include/linux/netfilter_ipv4/ip_conntrack_tuple.h 2004-04-28 03:36:22.000000000 +0200 +++ linux-2.6.6-rc3/include/linux/netfilter_ipv4/ip_conntrack_tuple.h 2004-04-29 11:20:17.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-rc3.org/include/linux/netfilter_ipv4/ip_tables.h linux-2.6.6-rc3/include/linux/netfilter_ipv4/ip_tables.h --- linux-2.6.6-rc3.org/include/linux/netfilter_ipv4/ip_tables.h 2004-04-28 03:35:48.000000000 +0200 +++ linux-2.6.6-rc3/include/linux/netfilter_ipv4/ip_tables.h 2004-04-29 11:17:15.000000000 +0200 @@ -134,6 +134,12 @@ /* Back pointer */ unsigned int comefrom; + /* Name of the chain */ + char *chainname; + + /* Rule number in the chain. */ + u_int32_t rulenum; + /* Packet and byte counters. */ struct ipt_counters counters; diff -Nur --exclude '*.orig' linux-2.6.6-rc3.org/include/linux/netfilter_ipv4/ipt_CONNMARK.h linux-2.6.6-rc3/include/linux/netfilter_ipv4/ipt_CONNMARK.h --- linux-2.6.6-rc3.org/include/linux/netfilter_ipv4/ipt_CONNMARK.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.6-rc3/include/linux/netfilter_ipv4/ipt_CONNMARK.h 2004-04-29 11:10:59.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-rc3.org/include/linux/netfilter_ipv4/ipt_IPMARK.h linux-2.6.6-rc3/include/linux/netfilter_ipv4/ipt_IPMARK.h --- linux-2.6.6-rc3.org/include/linux/netfilter_ipv4/ipt_IPMARK.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.6-rc3/include/linux/netfilter_ipv4/ipt_IPMARK.h 2004-04-29 11:17:03.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-rc3.org/include/linux/netfilter_ipv4/ipt_XOR.h linux-2.6.6-rc3/include/linux/netfilter_ipv4/ipt_XOR.h --- linux-2.6.6-rc3.org/include/linux/netfilter_ipv4/ipt_XOR.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.6-rc3/include/linux/netfilter_ipv4/ipt_XOR.h 2004-04-29 11:17:18.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-rc3.org/include/linux/netfilter_ipv4/ipt_addrtype.h linux-2.6.6-rc3/include/linux/netfilter_ipv4/ipt_addrtype.h --- linux-2.6.6-rc3.org/include/linux/netfilter_ipv4/ipt_addrtype.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.6-rc3/include/linux/netfilter_ipv4/ipt_addrtype.h 2004-04-29 11:17:23.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-rc3.org/include/linux/netfilter_ipv4/ipt_connmark.h linux-2.6.6-rc3/include/linux/netfilter_ipv4/ipt_connmark.h --- linux-2.6.6-rc3.org/include/linux/netfilter_ipv4/ipt_connmark.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.6-rc3/include/linux/netfilter_ipv4/ipt_connmark.h 2004-04-29 11:10:59.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-rc3.org/include/linux/netfilter_ipv4/ipt_policy.h linux-2.6.6-rc3/include/linux/netfilter_ipv4/ipt_policy.h --- linux-2.6.6-rc3.org/include/linux/netfilter_ipv4/ipt_policy.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.6-rc3/include/linux/netfilter_ipv4/ipt_policy.h 2004-04-29 11:19:41.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-rc3.org/include/linux/netfilter_ipv4/ipt_rpc.h linux-2.6.6-rc3/include/linux/netfilter_ipv4/ipt_rpc.h --- linux-2.6.6-rc3.org/include/linux/netfilter_ipv4/ipt_rpc.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.6-rc3/include/linux/netfilter_ipv4/ipt_rpc.h 2004-04-29 11:20:02.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-rc3.org/include/linux/netfilter_ipv4/ipt_string.h linux-2.6.6-rc3/include/linux/netfilter_ipv4/ipt_string.h --- linux-2.6.6-rc3.org/include/linux/netfilter_ipv4/ipt_string.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.6-rc3/include/linux/netfilter_ipv4/ipt_string.h 2004-04-29 11:21:38.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-rc3.org/include/linux/netfilter_ipv4.h linux-2.6.6-rc3/include/linux/netfilter_ipv4.h --- linux-2.6.6-rc3.org/include/linux/netfilter_ipv4.h 2004-04-28 03:36:31.000000000 +0200 +++ linux-2.6.6-rc3/include/linux/netfilter_ipv4.h 2004-04-29 11:18:03.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); + +#if defined(CONFIG_XFRM) && defined(CONFIG_NETFILTER) +#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-rc3.org/include/linux/netfilter_ipv6/ip6_tables.h linux-2.6.6-rc3/include/linux/netfilter_ipv6/ip6_tables.h --- linux-2.6.6-rc3.org/include/linux/netfilter_ipv6/ip6_tables.h 2004-04-28 03:36:37.000000000 +0200 +++ linux-2.6.6-rc3/include/linux/netfilter_ipv6/ip6_tables.h 2004-04-29 11:17:15.000000000 +0200 @@ -140,6 +140,12 @@ /* Back pointer */ unsigned int comefrom; + /* Name of the chain */ + char *chainname; + + /* Rule number in the chain. */ + u_int32_t rulenum; + /* Packet and byte counters. */ struct ip6t_counters counters; diff -Nur --exclude '*.orig' linux-2.6.6-rc3.org/include/linux/netfilter_ipv6/ip6t_owner.h linux-2.6.6-rc3/include/linux/netfilter_ipv6/ip6t_owner.h --- linux-2.6.6-rc3.org/include/linux/netfilter_ipv6/ip6t_owner.h 2004-04-28 03:36:03.000000000 +0200 +++ linux-2.6.6-rc3/include/linux/netfilter_ipv6/ip6t_owner.h 2004-04-29 11:19:40.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-rc3.org/include/linux/netfilter_ipv6/ip6t_policy.h linux-2.6.6-rc3/include/linux/netfilter_ipv6/ip6t_policy.h --- linux-2.6.6-rc3.org/include/linux/netfilter_ipv6/ip6t_policy.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.6-rc3/include/linux/netfilter_ipv6/ip6t_policy.h 2004-04-29 11:19:41.000000000 +0200 @@ -0,0 +1,52 @@ +#ifndef _IP6T_POLICY_H +#define _IP6T_POLICY_H + +#define POLICY_MAX_ELEM 4 + +enum ip6t_policy_flags +{ + POLICY_MATCH_IN = 0x1, + POLICY_MATCH_OUT = 0x2, + POLICY_MATCH_NONE = 0x4, + POLICY_MATCH_STRICT = 0x8, +}; + +enum ip6t_policy_modes +{ + POLICY_MODE_TRANSPORT, + POLICY_MODE_TUNNEL +}; + +struct ip6t_policy_spec +{ + u_int8_t saddr:1, + daddr:1, + proto:1, + mode:1, + spi:1, + reqid:1; +}; + +struct ip6t_policy_elem +{ + struct in6_addr saddr; + struct in6_addr smask; + struct in6_addr daddr; + struct in6_addr dmask; + u_int32_t spi; + u_int32_t reqid; + u_int8_t proto; + u_int8_t mode; + + struct ip6t_policy_spec match; + struct ip6t_policy_spec invert; +}; + +struct ip6t_policy_info +{ + struct ip6t_policy_elem pol[POLICY_MAX_ELEM]; + u_int16_t flags; + u_int16_t len; +}; + +#endif /* _IP6T_POLICY_H */ diff -Nur --exclude '*.orig' linux-2.6.6-rc3.org/include/linux/netfilter_mime.h linux-2.6.6-rc3/include/linux/netfilter_mime.h --- linux-2.6.6-rc3.org/include/linux/netfilter_mime.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.6-rc3/include/linux/netfilter_mime.h 2004-04-29 11:20:13.000000000 +0200 @@ -0,0 +1,89 @@ +/* + * MIME functions for netfilter modules. This file provides implementations + * for basic MIME parsing. MIME headers are used in many protocols, such as + * HTTP, RTSP, SIP, etc. + * + * gcc will warn for defined but unused functions, so we only include the + * functions requested. The following macros are used: + * NF_NEED_MIME_NEXTLINE nf_mime_nextline() + */ +#ifndef _NETFILTER_MIME_H +#define _NETFILTER_MIME_H + +/* Only include these functions for kernel code. */ +#ifdef __KERNEL__ + +#include + +/* + * Given a buffer and length, advance to the next line and mark the current + * line. If the current line is empty, *plinelen will be set to zero. If + * not, it will be set to the actual line length (including CRLF). + * + * 'line' in this context means logical line (includes LWS continuations). + * Returns 1 on success, 0 on failure. + */ +#ifdef NF_NEED_MIME_NEXTLINE +static int +nf_mime_nextline(char* p, uint len, uint* poff, uint* plineoff, uint* plinelen) +{ + uint off = *poff; + uint physlen = 0; + int is_first_line = 1; + + if (off >= len) + { + return 0; + } + + do + { + while (p[off] != '\n') + { + if (len-off <= 1) + { + return 0; + } + + physlen++; + off++; + } + + /* if we saw a crlf, physlen needs adjusted */ + if (physlen > 0 && p[off] == '\n' && p[off-1] == '\r') + { + physlen--; + } + + /* advance past the newline */ + off++; + + /* check for an empty line */ + if (physlen == 0) + { + break; + } + + /* check for colon on the first physical line */ + if (is_first_line) + { + is_first_line = 0; + if (memchr(p+(*poff), ':', physlen) == NULL) + { + return 0; + } + } + } + while (p[off] == ' ' || p[off] == '\t'); + + *plineoff = *poff; + *plinelen = (physlen == 0) ? 0 : (off - *poff); + *poff = off; + + return 1; +} +#endif /* NF_NEED_MIME_NEXTLINE */ + +#endif /* __KERNEL__ */ + +#endif /* _NETFILTER_MIME_H */ diff -Nur --exclude '*.orig' linux-2.6.6-rc3.org/include/net/ip.h linux-2.6.6-rc3/include/net/ip.h --- linux-2.6.6-rc3.org/include/net/ip.h 2004-04-28 03:36:16.000000000 +0200 +++ linux-2.6.6-rc3/include/net/ip.h 2004-04-29 11:18:02.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-rc3.org/include/net/protocol.h linux-2.6.6-rc3/include/net/protocol.h --- linux-2.6.6-rc3.org/include/net/protocol.h 2004-04-28 03:35:44.000000000 +0200 +++ linux-2.6.6-rc3/include/net/protocol.h 2004-04-29 11:18:03.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-rc3.org/include/net/tcp.h linux-2.6.6-rc3/include/net/tcp.h --- linux-2.6.6-rc3.org/include/net/tcp.h 2004-04-29 11:24:38.000000000 +0200 +++ linux-2.6.6-rc3/include/net/tcp.h 2004-04-29 11:19:32.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-rc3.org/include/net/udp.h linux-2.6.6-rc3/include/net/udp.h --- linux-2.6.6-rc3.org/include/net/udp.h 2004-04-28 03:36:29.000000000 +0200 +++ linux-2.6.6-rc3/include/net/udp.h 2004-04-29 11:19:32.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-rc3.org/include/net/xfrm.h linux-2.6.6-rc3/include/net/xfrm.h --- linux-2.6.6-rc3.org/include/net/xfrm.h 2004-04-28 03:36:31.000000000 +0200 +++ linux-2.6.6-rc3/include/net/xfrm.h 2004-04-29 11:18:03.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-rc3.org/net/core/netfilter.c linux-2.6.6-rc3/net/core/netfilter.c --- linux-2.6.6-rc3.org/net/core/netfilter.c 2004-04-28 03:35:46.000000000 +0200 +++ linux-2.6.6-rc3/net/core/netfilter.c 2004-04-29 11:18:06.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; @@ -839,3 +919,4 @@ EXPORT_SYMBOL(nf_unregister_hook); EXPORT_SYMBOL(nf_unregister_queue_handler); EXPORT_SYMBOL(nf_unregister_sockopt); +EXPORT_SYMBOL(nf_rcv_postxfrm_local); diff -Nur --exclude '*.orig' linux-2.6.6-rc3.org/net/ipv4/ah4.c linux-2.6.6-rc3/net/ipv4/ah4.c --- linux-2.6.6-rc3.org/net/ipv4/ah4.c 2004-04-28 03:36:34.000000000 +0200 +++ linux-2.6.6-rc3/net/ipv4/ah4.c 2004-04-29 11:18:03.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-rc3.org/net/ipv4/esp4.c linux-2.6.6-rc3/net/ipv4/esp4.c --- linux-2.6.6-rc3.org/net/ipv4/esp4.c 2004-04-28 03:36:01.000000000 +0200 +++ linux-2.6.6-rc3/net/ipv4/esp4.c 2004-04-29 11:18:03.000000000 +0200 @@ -216,6 +216,7 @@ err = -EHOSTUNREACH; goto error_nolock; } + IPCB(skb)->flags |= IPSKB_XFRM_TRANSFORMED; return NET_XMIT_BYPASS; error: @@ -598,6 +599,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-rc3.org/net/ipv4/igmp.c linux-2.6.6-rc3/net/ipv4/igmp.c --- linux-2.6.6-rc3.org/net/ipv4/igmp.c 2004-04-28 03:36:55.000000000 +0200 +++ linux-2.6.6-rc3/net/ipv4/igmp.c 2004-04-29 11:18:02.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-rc3.org/net/ipv4/ip_forward.c linux-2.6.6-rc3/net/ipv4/ip_forward.c --- linux-2.6.6-rc3.org/net/ipv4/ip_forward.c 2004-04-28 03:34:59.000000000 +0200 +++ linux-2.6.6-rc3/net/ipv4/ip_forward.c 2004-04-29 11:18:03.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-rc3.org/net/ipv4/ip_input.c linux-2.6.6-rc3/net/ipv4/ip_input.c --- linux-2.6.6-rc3.org/net/ipv4/ip_input.c 2004-04-29 11:24:39.000000000 +0200 +++ linux-2.6.6-rc3/net/ipv4/ip_input.c 2004-04-29 11:18:06.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-rc3.org/net/ipv4/ip_output.c linux-2.6.6-rc3/net/ipv4/ip_output.c --- linux-2.6.6-rc3.org/net/ipv4/ip_output.c 2004-04-28 03:36:55.000000000 +0200 +++ linux-2.6.6-rc3/net/ipv4/ip_output.c 2004-04-29 11:18:03.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-rc3.org/net/ipv4/ipcomp.c linux-2.6.6-rc3/net/ipv4/ipcomp.c --- linux-2.6.6-rc3.org/net/ipv4/ipcomp.c 2004-04-28 03:36:33.000000000 +0200 +++ linux-2.6.6-rc3/net/ipv4/ipcomp.c 2004-04-29 11:18:03.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-rc3.org/net/ipv4/ipip.c linux-2.6.6-rc3/net/ipv4/ipip.c --- linux-2.6.6-rc3.org/net/ipv4/ipip.c 2004-04-29 11:24:39.000000000 +0200 +++ linux-2.6.6-rc3/net/ipv4/ipip.c 2004-04-29 11:18:03.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-rc3.org/net/ipv4/ipmr.c linux-2.6.6-rc3/net/ipv4/ipmr.c --- linux-2.6.6-rc3.org/net/ipv4/ipmr.c 2004-04-28 03:35:47.000000000 +0200 +++ linux-2.6.6-rc3/net/ipv4/ipmr.c 2004-04-29 11:18:03.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-rc3.org/net/ipv4/netfilter/Kconfig linux-2.6.6-rc3/net/ipv4/netfilter/Kconfig --- linux-2.6.6-rc3.org/net/ipv4/netfilter/Kconfig 2004-04-29 11:24:40.000000000 +0200 +++ linux-2.6.6-rc3/net/ipv4/netfilter/Kconfig 2004-04-29 11:21:43.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 @@ -706,5 +711,149 @@ depends on IP_NF_IPTABLES help +config IP_NF_CONNTRACK_MARK + bool 'Connection mark tracking support' +config IP_NF_TARGET_CONNMARK + tristate 'CONNMARK target support' + depends on IP_NF_MANGLE +config IP_NF_MATCH_CONNMARK + tristate ' Connection mark match support' + depends on IP_NF_IPTABLES + help + +config IP_NF_TARGET_IPMARK + tristate 'IPMARK target support' + depends on IP_NF_MANGLE + help + +config IP_NF_TARGET_TARPIT + tristate 'TARPIT target support' + depends on IP_NF_FILTER + help + +config IP_NF_TARGET_TRACE + tristate 'TRACE target support' + depends on IP_NF_RAW + help + The TRACE target allows packets to be traced as those + matches any subsequent rule in any table/rule. The matched + rule and the packet is logged with the prefix + + TRACE: tablename/chainname/rulenum + + If you want to compile it as a module, say M here and read + . If unsure, say `N'. + 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_CUSEEME + tristate + depends on IP_NF_CONNTRACK!=n && IP_NF_NAT!=n + default IP_NF_NAT if IP_NF_CUSEEME=y + default m if IP_NF_CUSEEME=m + +config IP_NF_CUSEEME + tristate 'CuSeeMe protocol support' + depends on IP_NF_CONNTRACK + help + +config IP_NF_EGG + tristate 'Eggdrop bot support' + depends on IP_NF_CONNTRACK + 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_NAT_RTSP + tristate + depends on IP_NF_CONNTRACK!=n && IP_NF_NAT!=n + default IP_NF_NAT if IP_NF_RTSP=y + default m if IP_NF_RTSP=m +config IP_NF_RTSP + tristate ' RTSP 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_MATCH_STRING + tristate 'String match support' + depends on IP_NF_IPTABLES + 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 + endmenu diff -Nur --exclude '*.orig' linux-2.6.6-rc3.org/net/ipv4/netfilter/Makefile linux-2.6.6-rc3/net/ipv4/netfilter/Makefile --- linux-2.6.6-rc3.org/net/ipv4/netfilter/Makefile 2004-04-29 11:24:40.000000000 +0200 +++ linux-2.6.6-rc3/net/ipv4/netfilter/Makefile 2004-04-29 11:21:43.000000000 +0200 @@ -19,17 +19,44 @@ # connection tracking obj-$(CONFIG_IP_NF_CONNTRACK) += ip_conntrack.o +# talk protocol support +obj-$(CONFIG_IP_NF_TALK) += ip_conntrack_talk.o +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 +obj-$(CONFIG_IP_NF_NAT_H323) += ip_nat_h323.o + + # connection tracking helpers + +# rtsp protocol support +obj-$(CONFIG_IP_NF_RTSP) += ip_conntrack_rtsp.o +obj-$(CONFIG_IP_NF_NAT_RTSP) += ip_nat_rtsp.o + +obj-$(CONFIG_IP_NF_QUAKE3) += ip_conntrack_quake3.o +obj-$(CONFIG_IP_NF_MMS) += ip_conntrack_mms.o 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_EGG) += ip_conntrack_egg.o + obj-$(CONFIG_IP_NF_IRC) += ip_conntrack_irc.o # NAT helpers +obj-$(CONFIG_IP_NF_NAT_CUSEEME) += ip_nat_cuseeme.o obj-$(CONFIG_IP_NF_NAT_AMANDA) += ip_nat_amanda.o 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 +83,8 @@ 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 + 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 +132,19 @@ obj-$(CONFIG_IP_NF_MATCH_U32) += ipt_u32.o +obj-$(CONFIG_IP_NF_MATCH_UNCLEAN) += ipt_unclean.o +obj-$(CONFIG_IP_NF_MATCH_STRING) += ipt_string.o obj-$(CONFIG_IP_NF_MATCH_TTL) += ipt_ttl.o obj-$(CONFIG_IP_NF_MATCH_STATE) += ipt_state.o +obj-$(CONFIG_IP_NF_MATCH_CONNMARK) += ipt_connmark.o obj-$(CONFIG_IP_NF_MATCH_CONNLIMIT) += ipt_connlimit.o obj-$(CONFIG_IP_NF_MATCH_CONNTRACK) += ipt_conntrack.o obj-$(CONFIG_IP_NF_MATCH_TCPMSS) += ipt_tcpmss.o +obj-$(CONFIG_IP_NF_MATCH_ADDRTYPE) += ipt_addrtype.o obj-$(CONFIG_IP_NF_MATCH_REALM) += ipt_realm.o obj-$(CONFIG_IP_NF_MATCH_PHYSDEV) += ipt_physdev.o +obj-$(CONFIG_IP_NF_MATCH_POLICY) += ipt_policy.o # targets obj-$(CONFIG_IP_NF_TARGET_REJECT) += ipt_REJECT.o @@ -102,6 +152,8 @@ obj-$(CONFIG_IP_NF_TARGET_ECN) += ipt_ECN.o obj-$(CONFIG_IP_NF_TARGET_DSCP) += ipt_DSCP.o obj-$(CONFIG_IP_NF_TARGET_MARK) += ipt_MARK.o +obj-$(CONFIG_IP_NF_TARGET_TARPIT) += ipt_TARPIT.o +obj-$(CONFIG_IP_NF_TARGET_IPMARK) += ipt_IPMARK.o obj-$(CONFIG_IP_NF_TARGET_IMQ) += ipt_IMQ.o obj-$(CONFIG_IP_NF_TARGET_MASQUERADE) += ipt_MASQUERADE.o obj-$(CONFIG_IP_NF_TARGET_REDIRECT) += ipt_REDIRECT.o @@ -110,11 +162,14 @@ obj-$(CONFIG_IP_NF_TARGET_CLASSIFY) += ipt_CLASSIFY.o obj-$(CONFIG_IP_NF_NAT_SNMP_BASIC) += ip_nat_snmp_basic.o obj-$(CONFIG_IP_NF_TARGET_LOG) += ipt_LOG.o +obj-$(CONFIG_IP_NF_TARGET_XOR) += ipt_XOR.o +obj-$(CONFIG_IP_NF_TARGET_CONNMARK) += ipt_CONNMARK.o obj-$(CONFIG_IP_NF_TARGET_TTL) += ipt_TTL.o obj-$(CONFIG_IP_NF_TARGET_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_TCPMSS) += ipt_TCPMSS.o +obj-$(CONFIG_IP_NF_TARGET_TRACE) += ipt_TRACE.o obj-$(CONFIG_IP_NF_TARGET_NOTRACK) += ipt_NOTRACK.o # generic ARP tables diff -Nur --exclude '*.orig' linux-2.6.6-rc3.org/net/ipv4/netfilter/ip_conntrack_core.c linux-2.6.6-rc3/net/ipv4/netfilter/ip_conntrack_core.c --- linux-2.6.6-rc3.org/net/ipv4/netfilter/ip_conntrack_core.c 2004-04-29 11:24:39.000000000 +0200 +++ linux-2.6.6-rc3/net/ipv4/netfilter/ip_conntrack_core.c 2004-04-29 11:11:00.000000000 +0200 @@ -718,6 +718,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-rc3.org/net/ipv4/netfilter/ip_conntrack_egg.c linux-2.6.6-rc3/net/ipv4/netfilter/ip_conntrack_egg.c --- linux-2.6.6-rc3.org/net/ipv4/netfilter/ip_conntrack_egg.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.6-rc3/net/ipv4/netfilter/ip_conntrack_egg.c 2004-04-29 11:17:59.000000000 +0200 @@ -0,0 +1,237 @@ +/* Eggdrop extension for IP connection tracking, Version 0.0.5 + * based on ip_conntrack_irc.c + * + * This module only supports the share userfile-send command, + * used by eggdrops to share it's userfile. + * + * There are no support for NAT at the moment. + * + * 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: + * + * please give the ports of all Eggdrops You have running + * on your system, the default port is 3333. + * + * 2001-04-19: Security update. IP addresses are now compared + * to prevent unauthorized "related" access. + * + * 2002-03-25: Harald Welte : + * Port to netfilter 'newnat' API. + */ + +#include +#include +#include +#include +#include + +#include +#include + +#define MAX_PORTS 8 +static int ports[MAX_PORTS]; +static int ports_c = 0; +static unsigned int egg_timeout = 300; + +MODULE_AUTHOR("Magnus Sandin "); +MODULE_DESCRIPTION("Eggdrop (userfile-sharing) 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 eggdrop servers"); +#endif + +DECLARE_LOCK(ip_egg_lock); +struct module *ip_conntrack_egg = THIS_MODULE; + +#if 0 +#define DEBUGP printk +#else +#define DEBUGP(format, args...) +#endif + +int parse_command(char *data, char *data_end, u_int32_t * ip, u_int16_t * port) +/* tries to get the ip_addr and port out of a eggdrop command + return value: -1 on failure, 0 on success + data pointer to first byte of DCC command data + data_end pointer to last byte of dcc command data + ip returns parsed ip of dcc command + port returns parsed port of dcc command */ +{ + if (data > data_end) + return -1; + + *ip = simple_strtoul(data, &data, 10); + + /* skip blanks between ip and port */ + while (*data == ' ' && data < data_end) + data++; + + *port = simple_strtoul(data, &data, 10); + return 0; +} + + +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; + char *data = (char *) tcph + tcph->doff * 4; + char *data_limit; + u_int32_t tcplen = len - iph->ihl * 4; + u_int32_t datalen = tcplen - tcph->doff * 4; + int dir = CTINFO2DIR(ctinfo); + int bytes_scanned = 0; + struct ip_conntrack_expect exp; + + u_int32_t egg_ip; + u_int16_t egg_port; + + DEBUGP("entered\n"); + + /* If packet is coming from IRC server */ + 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; + } + + data_limit = (char *) data + datalen; + while (datalen > 5 && bytes_scanned < 128) { + if (memcmp(data, "s us ", 5)) { + data++; + datalen--; + bytes_scanned++; + continue; + } + + data += 5; + + DEBUGP("Userfile-share found in connection " + "%u.%u.%u.%u -> %u.%u.%u.%u\n", + NIPQUAD(iph->saddr), NIPQUAD(iph->daddr)); + + if (parse_command((char *) data, data_limit, &egg_ip, + &egg_port)) { + DEBUGP("no data in userfile-share pkt\n"); + return NF_ACCEPT; + } + + memset(&exp, 0, sizeof(exp)); + + if (ct->tuplehash[dir].tuple.src.ip != htonl(egg_ip)) { + if (net_ratelimit()) + printk("Forged Eggdrop command from " + "%u.%u.%u.%u: %u.%u.%u.%u:%u\n", + NIPQUAD(ct->tuplehash[dir].tuple.src.ip), + HIPQUAD(egg_ip), egg_port); + return NF_ACCEPT; + } + + exp.tuple.src.ip = iph->daddr; + exp.tuple.src.u.tcp.port = 0; + exp.tuple.dst.ip = htonl(egg_ip); + exp.tuple.dst.u.tcp.port = htons(egg_port); + exp.tuple.dst.protonum = IPPROTO_TCP; + + exp.mask.dst.u.tcp.port = 0xffff; + exp.mask.dst.protonum = 0xffff; + + DEBUGP("expect_related %u.%u.%u.%u:%u - %u.%u.%u.%u:%u\n", + NIPQUAD(t.src.ip), ntohs(t.src.u.tcp.port), + NIPQUAD(t.dst.ip), ntohs(t.dst.u.tcp.port)); + + ip_conntrack_expect_related(ct, &exp); + break; + } + return NF_ACCEPT; +} + +static struct ip_conntrack_helper egg_helpers[MAX_PORTS]; +static char egg_names[MAX_PORTS][14]; /* eggdrop-65535 */ + +static void deregister_helpers(void) { + int i; + + for (i = 0; i < ports_c; i++) { + DEBUGP("unregistering helper for port %d\n", ports[i]); + ip_conntrack_helper_unregister(&egg_helpers[i]); + } +} + +static int __init init(void) +{ + int i, ret; + char *tmpname; + + /* If no port given, default to standard eggdrop port */ + if (ports[0] == 0) + ports[0] = 3333; + + for (i = 0; (i < MAX_PORTS) && ports[i]; i++) { + memset(&egg_helpers[i], 0, + sizeof(struct ip_conntrack_helper)); + egg_helpers[i].tuple.src.u.tcp.port = htons(ports[i]); + egg_helpers[i].tuple.dst.protonum = IPPROTO_TCP; + egg_helpers[i].mask.src.u.tcp.port = 0xFFFF; + egg_helpers[i].mask.dst.protonum = 0xFFFF; + egg_helpers[i].max_expected = 1; + egg_helpers[i].timeout = egg_timeout; + egg_helpers[i].flags = IP_CT_HELPER_F_REUSE_EXPECT; + egg_helpers[i].me = THIS_MODULE; + egg_helpers[i].help = help; + + tmpname = &egg_names[i][0]; + if (ports[i] == 3333) + sprintf(tmpname, "eggdrop"); + else + sprintf(tmpname, "eggdrop-%d", ports[i]); + egg_helpers[i].name = tmpname; + + DEBUGP("port #%d: %d\n", i, ports[i]); + + ret = ip_conntrack_helper_register(&egg_helpers[i]); + + if (ret) { + printk("ip_conntrack_egg: ERROR registering helper " + "for port %d\n", ports[i]); + deregister_helpers(); + return 1; + } + ports_c++; + } + return 0; +} + +static void __exit fini(void) +{ + deregister_helpers(); +} + +module_init(init); +module_exit(fini); diff -Nur --exclude '*.orig' linux-2.6.6-rc3.org/net/ipv4/netfilter/ip_conntrack_h323.c linux-2.6.6-rc3/net/ipv4/netfilter/ip_conntrack_h323.c --- linux-2.6.6-rc3.org/net/ipv4/netfilter/ip_conntrack_h323.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.6-rc3/net/ipv4/netfilter/ip_conntrack_h323.c 2004-04-29 11:18:00.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-rc3.org/net/ipv4/netfilter/ip_conntrack_mms.c linux-2.6.6-rc3/net/ipv4/netfilter/ip_conntrack_mms.c --- linux-2.6.6-rc3.org/net/ipv4/netfilter/ip_conntrack_mms.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.6-rc3/net/ipv4/netfilter/ip_conntrack_mms.c 2004-04-29 11:19:01.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-rc3.org/net/ipv4/netfilter/ip_conntrack_proto_sctp.c linux-2.6.6-rc3/net/ipv4/netfilter/ip_conntrack_proto_sctp.c --- linux-2.6.6-rc3.org/net/ipv4/netfilter/ip_conntrack_proto_sctp.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.6-rc3/net/ipv4/netfilter/ip_conntrack_proto_sctp.c 2004-04-29 11:20:17.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-rc3.org/net/ipv4/netfilter/ip_conntrack_quake3.c linux-2.6.6-rc3/net/ipv4/netfilter/ip_conntrack_quake3.c --- linux-2.6.6-rc3.org/net/ipv4/netfilter/ip_conntrack_quake3.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.6-rc3/net/ipv4/netfilter/ip_conntrack_quake3.c 2004-04-29 11:19:53.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-rc3.org/net/ipv4/netfilter/ip_conntrack_rpc_tcp.c linux-2.6.6-rc3/net/ipv4/netfilter/ip_conntrack_rpc_tcp.c --- linux-2.6.6-rc3.org/net/ipv4/netfilter/ip_conntrack_rpc_tcp.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.6-rc3/net/ipv4/netfilter/ip_conntrack_rpc_tcp.c 2004-04-29 11:20:02.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-rc3.org/net/ipv4/netfilter/ip_conntrack_rpc_udp.c linux-2.6.6-rc3/net/ipv4/netfilter/ip_conntrack_rpc_udp.c --- linux-2.6.6-rc3.org/net/ipv4/netfilter/ip_conntrack_rpc_udp.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.6-rc3/net/ipv4/netfilter/ip_conntrack_rpc_udp.c 2004-04-29 11:20:02.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-rc3.org/net/ipv4/netfilter/ip_conntrack_rsh.c linux-2.6.6-rc3/net/ipv4/netfilter/ip_conntrack_rsh.c --- linux-2.6.6-rc3.org/net/ipv4/netfilter/ip_conntrack_rsh.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.6-rc3/net/ipv4/netfilter/ip_conntrack_rsh.c 2004-04-29 11:20:06.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-rc3.org/net/ipv4/netfilter/ip_conntrack_rtsp.c linux-2.6.6-rc3/net/ipv4/netfilter/ip_conntrack_rtsp.c --- linux-2.6.6-rc3.org/net/ipv4/netfilter/ip_conntrack_rtsp.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.6-rc3/net/ipv4/netfilter/ip_conntrack_rtsp.c 2004-04-29 11:20:13.000000000 +0200 @@ -0,0 +1,507 @@ +/* + * RTSP extension for IP connection tracking + * (C) 2003 by Tom Marshall + * based on ip_conntrack_irc.c + * + * 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_rtsp.o ports=port1,port2,...port + * max_outstanding=n setup_timeout=secs + * + * If no ports are specified, the default will be port 554. + * + * With max_outstanding you can define the maximum number of not yet + * answered SETUP requests per RTSP session (default 8). + * With setup_timeout you can specify how long the system waits for + * an expected data channel (default 300 seconds). + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#define NF_NEED_STRNCASECMP +#define NF_NEED_STRTOU16 +#define NF_NEED_STRTOU32 +#define NF_NEED_NEXTLINE +#include +#define NF_NEED_MIME_NEXTLINE +#include + +#define MAX_SIMUL_SETUP 8 /* XXX: use max_outstanding */ + +#define INFOP(fmt, args...) printk(KERN_INFO "%s: %s: " fmt, __FILE__, __FUNCTION__, ## args) +#ifdef IP_NF_RTSP_DEBUG +#define DEBUGP(fmt, args...) printk(KERN_DEBUG "%s: %s: " fmt, __FILE__, __FUNCTION__, ## args) +#else +#define DEBUGP(fmt, args...) +#endif + +#define MAX_PORTS 8 +static int ports[MAX_PORTS]; +static int num_ports = 0; +static int max_outstanding = 8; +static unsigned int setup_timeout = 300; + +MODULE_AUTHOR("Tom Marshall "); +MODULE_DESCRIPTION("RTSP 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 RTSP servers"); +MODULE_PARM(max_outstanding, "i"); +MODULE_PARM_DESC(max_outstanding, "max number of outstanding SETUP requests per RTSP session"); +MODULE_PARM(setup_timeout, "i"); +MODULE_PARM_DESC(setup_timeout, "timeout on for unestablished data channels"); +#endif + +DECLARE_LOCK(ip_rtsp_lock); +struct module* ip_conntrack_rtsp = THIS_MODULE; + +/* + * Max mappings we will allow for one RTSP connection (for RTP, the number + * of allocated ports is twice this value). Note that SMIL burns a lot of + * ports so keep this reasonably high. If this is too low, you will see a + * lot of "no free client map entries" messages. + */ +#define MAX_PORT_MAPS 16 + +/*** default port list was here in the masq code: 554, 3030, 4040 ***/ + +#define SKIP_WSPACE(ptr,len,off) while(off < len && isspace(*(ptr+off))) { off++; } + +/* + * Parse an RTSP packet. + * + * Returns zero if parsing failed. + * + * Parameters: + * IN ptcp tcp data pointer + * IN tcplen tcp data len + * IN/OUT ptcpoff points to current tcp offset + * OUT phdrsoff set to offset of rtsp headers + * OUT phdrslen set to length of rtsp headers + * OUT pcseqoff set to offset of CSeq header + * OUT pcseqlen set to length of CSeq header + */ +static int +rtsp_parse_message(char* ptcp, uint tcplen, uint* ptcpoff, + uint* phdrsoff, uint* phdrslen, + uint* pcseqoff, uint* pcseqlen) +{ + uint entitylen = 0; + uint lineoff; + uint linelen; + + if (!nf_nextline(ptcp, tcplen, ptcpoff, &lineoff, &linelen)) + { + return 0; + } + + *phdrsoff = *ptcpoff; + while (nf_mime_nextline(ptcp, tcplen, ptcpoff, &lineoff, &linelen)) + { + if (linelen == 0) + { + if (entitylen > 0) + { + *ptcpoff += min(entitylen, tcplen - *ptcpoff); + } + break; + } + if (lineoff+linelen > tcplen) + { + INFOP("!! overrun !!\n"); + break; + } + + if (nf_strncasecmp(ptcp+lineoff, "CSeq:", 5) == 0) + { + *pcseqoff = lineoff; + *pcseqlen = linelen; + } + if (nf_strncasecmp(ptcp+lineoff, "Content-Length:", 15) == 0) + { + uint off = lineoff+15; + SKIP_WSPACE(ptcp+lineoff, linelen, off); + nf_strtou32(ptcp+off, &entitylen); + } + } + *phdrslen = (*ptcpoff) - (*phdrsoff); + + return 1; +} + +/* + * Find lo/hi client ports (if any) in transport header + * In: + * ptcp, tcplen = packet + * tranoff, tranlen = buffer to search + * + * Out: + * pport_lo, pport_hi = lo/hi ports (host endian) + * + * Returns nonzero if any client ports found + * + * Note: it is valid (and expected) for the client to request multiple + * transports, so we need to parse the entire line. + */ +static int +rtsp_parse_transport(char* ptran, uint tranlen, + struct ip_ct_rtsp_expect* prtspexp) +{ + int rc = 0; + uint off = 0; + + if (tranlen < 10 || !iseol(ptran[tranlen-1]) || + nf_strncasecmp(ptran, "Transport:", 10) != 0) + { + INFOP("sanity check failed\n"); + return 0; + } + DEBUGP("tran='%.*s'\n", (int)tranlen, ptran); + off += 10; + SKIP_WSPACE(ptran, tranlen, off); + + /* Transport: tran;field;field=val,tran;field;field=val,... */ + while (off < tranlen) + { + const char* pparamend; + uint nextparamoff; + + pparamend = memchr(ptran+off, ',', tranlen-off); + pparamend = (pparamend == NULL) ? ptran+tranlen : pparamend+1; + nextparamoff = pparamend-ptran; + + while (off < nextparamoff) + { + const char* pfieldend; + uint nextfieldoff; + + pfieldend = memchr(ptran+off, ';', nextparamoff-off); + nextfieldoff = (pfieldend == NULL) ? nextparamoff : pfieldend-ptran+1; + + if (strncmp(ptran+off, "client_port=", 12) == 0) + { + u_int16_t port; + uint numlen; + + off += 12; + numlen = nf_strtou16(ptran+off, &port); + off += numlen; + if (prtspexp->loport != 0 && prtspexp->loport != port) + { + DEBUGP("multiple ports found, port %hu ignored\n", port); + } + else + { + prtspexp->loport = prtspexp->hiport = port; + if (ptran[off] == '-') + { + off++; + numlen = nf_strtou16(ptran+off, &port); + off += numlen; + prtspexp->pbtype = pb_range; + prtspexp->hiport = port; + + // If we have a range, assume rtp: + // loport must be even, hiport must be loport+1 + if ((prtspexp->loport & 0x0001) != 0 || + prtspexp->hiport != prtspexp->loport+1) + { + DEBUGP("incorrect range: %hu-%hu, correcting\n", + prtspexp->loport, prtspexp->hiport); + prtspexp->loport &= 0xfffe; + prtspexp->hiport = prtspexp->loport+1; + } + } + else if (ptran[off] == '/') + { + off++; + numlen = nf_strtou16(ptran+off, &port); + off += numlen; + prtspexp->pbtype = pb_discon; + prtspexp->hiport = port; + } + rc = 1; + } + } + + /* + * Note we don't look for the destination parameter here. + * If we are using NAT, the NAT module will handle it. If not, + * and the client is sending packets elsewhere, the expectation + * will quietly time out. + */ + + off = nextfieldoff; + } + + off = nextparamoff; + } + + return rc; +} + +/*** conntrack functions ***/ + +/* outbound packet: client->server */ +static int +help_out(const struct iphdr* iph, size_t pktlen, + struct ip_conntrack* ct, enum ip_conntrack_info ctinfo) +{ + int dir = CTINFO2DIR(ctinfo); /* = IP_CT_DIR_ORIGINAL */ + struct tcphdr* tcph = (void*)iph + iph->ihl * 4; + uint tcplen = pktlen - iph->ihl * 4; + char* pdata = (char*)tcph + tcph->doff * 4; + uint datalen = tcplen - tcph->doff * 4; + uint dataoff = 0; + + struct ip_conntrack_expect exp; + + while (dataoff < datalen) + { + uint cmdoff = dataoff; + uint hdrsoff = 0; + uint hdrslen = 0; + uint cseqoff = 0; + uint cseqlen = 0; + uint lineoff = 0; + uint linelen = 0; + uint off; + int rc; + + if (!rtsp_parse_message(pdata, datalen, &dataoff, + &hdrsoff, &hdrslen, + &cseqoff, &cseqlen)) + { + break; /* not a valid message */ + } + + if (strncmp(pdata+cmdoff, "SETUP ", 6) != 0) + { + continue; /* not a SETUP message */ + } + DEBUGP("found a setup message\n"); + + memset(&exp, 0, sizeof(exp)); + + off = 0; + while (nf_mime_nextline(pdata+hdrsoff, hdrslen, &off, + &lineoff, &linelen)) + { + if (linelen == 0) + { + break; + } + if (off > hdrsoff+hdrslen) + { + INFOP("!! overrun !!"); + break; + } + + if (nf_strncasecmp(pdata+hdrsoff+lineoff, "Transport:", 10) == 0) + { + rtsp_parse_transport(pdata+hdrsoff+lineoff, linelen, + &exp.help.exp_rtsp_info); + } + } + + if (exp.help.exp_rtsp_info.loport == 0) + { + DEBUGP("no udp transports found\n"); + continue; /* no udp transports found */ + } + + DEBUGP("udp transport found, ports=(%d,%hu,%hu)\n", + (int)exp.help.exp_rtsp_info.pbtype, + exp.help.exp_rtsp_info.loport, + exp.help.exp_rtsp_info.hiport); + + LOCK_BH(&ip_rtsp_lock); + exp.seq = ntohl(tcph->seq) + hdrsoff; /* mark all the headers */ + exp.help.exp_rtsp_info.len = hdrslen; + + exp.tuple.src.ip = ct->tuplehash[!dir].tuple.src.ip; + exp.mask.src.ip = 0xffffffff; + exp.tuple.dst.ip = ct->tuplehash[dir].tuple.src.ip; + exp.mask.dst.ip = 0xffffffff; + exp.tuple.dst.u.udp.port = exp.help.exp_rtsp_info.loport; + exp.mask.dst.u.udp.port = (exp.help.exp_rtsp_info.pbtype == pb_range) ? 0xfffe : 0xffff; + exp.tuple.dst.protonum = IPPROTO_UDP; + exp.mask.dst.protonum = 0xffff; + + DEBUGP("expect_related %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)); + + /* pass the request off to the nat helper */ + rc = ip_conntrack_expect_related(ct, &exp); + UNLOCK_BH(&ip_rtsp_lock); + if (rc == 0) + { + DEBUGP("ip_conntrack_expect_related succeeded\n"); + } + else + { + INFOP("ip_conntrack_expect_related failed (%d)\n", rc); + } + } + + return NF_ACCEPT; +} + +/* inbound packet: server->client */ +static int +help_in(const struct iphdr* iph, size_t pktlen, + struct ip_conntrack* ct, enum ip_conntrack_info ctinfo) +{ + return NF_ACCEPT; +} + +static int +help(const struct iphdr* iph, size_t pktlen, + 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; + u_int32_t tcplen = pktlen - iph->ihl * 4; + + /* 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; + } + + switch (CTINFO2DIR(ctinfo)) + { + case IP_CT_DIR_ORIGINAL: + help_out(iph, pktlen, ct, ctinfo); + break; + case IP_CT_DIR_REPLY: + help_in(iph, pktlen, ct, ctinfo); + break; + } + + return NF_ACCEPT; +} + +static struct ip_conntrack_helper rtsp_helpers[MAX_PORTS]; +static char rtsp_names[MAX_PORTS][10]; + +/* This function is intentionally _NOT_ defined as __exit */ +static void +fini(void) +{ + int i; + for (i = 0; i < num_ports; i++) + { + DEBUGP("unregistering port %d\n", ports[i]); + ip_conntrack_helper_unregister(&rtsp_helpers[i]); + } +} + +static int __init +init(void) +{ + int i, ret; + struct ip_conntrack_helper *hlpr; + char *tmpname; + + printk("ip_conntrack_rtsp v" IP_NF_RTSP_VERSION " loading\n"); + + if (max_outstanding < 1) + { + printk("ip_conntrack_rtsp: max_outstanding must be a positive integer\n"); + return -EBUSY; + } + if (setup_timeout < 0) + { + printk("ip_conntrack_rtsp: setup_timeout must be a positive integer\n"); + return -EBUSY; + } + + /* If no port given, default to standard rtsp port */ + if (ports[0] == 0) + { + ports[0] = RTSP_PORT; + } + + for (i = 0; (i < MAX_PORTS) && ports[i]; i++) + { + hlpr = &rtsp_helpers[i]; + memset(hlpr, 0, sizeof(struct ip_conntrack_helper)); + hlpr->tuple.src.u.tcp.port = htons(ports[i]); + hlpr->tuple.dst.protonum = IPPROTO_TCP; + hlpr->mask.src.u.tcp.port = 0xFFFF; + hlpr->mask.dst.protonum = 0xFFFF; + hlpr->max_expected = max_outstanding; + hlpr->timeout = setup_timeout; + hlpr->flags = IP_CT_HELPER_F_REUSE_EXPECT; + hlpr->me = ip_conntrack_rtsp; + hlpr->help = help; + + tmpname = &rtsp_names[i][0]; + if (ports[i] == RTSP_PORT) + { + sprintf(tmpname, "rtsp"); + } + else + { + sprintf(tmpname, "rtsp-%d", i); + } + hlpr->name = tmpname; + + DEBUGP("port #%d: %d\n", i, ports[i]); + + ret = ip_conntrack_helper_register(hlpr); + + if (ret) + { + printk("ip_conntrack_rtsp: ERROR registering port %d\n", ports[i]); + fini(); + return -EBUSY; + } + num_ports++; + } + return 0; +} + +#ifdef CONFIG_IP_NF_NAT_NEEDED +EXPORT_SYMBOL(ip_rtsp_lock); +#endif + +module_init(init); +module_exit(fini); diff -Nur --exclude '*.orig' linux-2.6.6-rc3.org/net/ipv4/netfilter/ip_conntrack_standalone.c linux-2.6.6-rc3/net/ipv4/netfilter/ip_conntrack_standalone.c --- linux-2.6.6-rc3.org/net/ipv4/netfilter/ip_conntrack_standalone.c 2004-04-28 03:35:45.000000000 +0200 +++ linux-2.6.6-rc3/net/ipv4/netfilter/ip_conntrack_standalone.c 2004-04-29 11:18:04.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-rc3.org/net/ipv4/netfilter/ip_conntrack_talk.c linux-2.6.6-rc3/net/ipv4/netfilter/ip_conntrack_talk.c --- linux-2.6.6-rc3.org/net/ipv4/netfilter/ip_conntrack_talk.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.6-rc3/net/ipv4/netfilter/ip_conntrack_talk.c 2004-04-29 11:21:43.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-rc3.org/net/ipv4/netfilter/ip_nat_cuseeme.c linux-2.6.6-rc3/net/ipv4/netfilter/ip_nat_cuseeme.c --- linux-2.6.6-rc3.org/net/ipv4/netfilter/ip_nat_cuseeme.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.6-rc3/net/ipv4/netfilter/ip_nat_cuseeme.c 2004-04-29 11:17:55.000000000 +0200 @@ -0,0 +1,289 @@ +/* CuSeeMe extension for UDP NAT alteration. + * (C) 2002 by Filip Sneppe + * based on ip_masq_cuseeme.c in 2.2 kernels + * + * ip_nat_cuseeme.c v0.0.7 2003-02-18 + * + * 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_cuseeme.o ports=port1,port2,...port + * + * Please give the ports of the CuSeeMe traffic you want to track. + * If you don't specify ports, the default will be UDP port 7648. + * + * CuSeeMe Protocol Documentation: + * http://cu-seeme.net/squeek/tech/contents.html + * + */ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +MODULE_AUTHOR("Filip Sneppe "); +MODULE_DESCRIPTION("Netfilter NAT helper for CuSeeMe"); +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 CuSeeMe reflectors"); +#endif + +#if 0 +#define DEBUGP printk +#else +#define DEBUGP(format, args...) +#endif + +/* process packet from client->reflector, possibly manipulate client IP in payload */ +void cuseeme_mangle_outgoing(struct ip_conntrack *ct, + struct ip_nat_info *info, + enum ip_conntrack_info ctinfo, + struct sk_buff **pskb, + char *data, + unsigned int datalen) +{ + char new_port_ip[6]; + struct cu_header *cu_head=(struct cu_header *)data; + + DEBUGP("ip_nat_cuseeme: outgoing packet, ID %u, dest_family %u\n", + ntohs(cu_head->data_type), ntohs(cu_head->dest_family)); + + /* At least check that the data at offset 10 is the client's port and IP address */ + if ((ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.ip == cu_head->addr) && + (ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.u.udp.port == cu_head->port)) { + DEBUGP("ip_nat_cuseeme: rewrite outgoing client %u.%u.%u.%u:%u->%u.%u.%u.%u:%u at offset 10\n", + NIPQUAD(cu_head->addr), + ntohs(cu_head->port), + NIPQUAD(ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.ip), + ntohs(ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.u.udp.port)); + *((u_int16_t *)new_port_ip) = ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.u.udp.port; + *((u_int32_t *)(new_port_ip+2)) = ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.ip; + /* at offset 10, replace 6 bytes containing port + IP address */ + ip_nat_mangle_udp_packet(pskb, ct, ctinfo, + 10, 6, (char *)(new_port_ip), 6); + } else + DEBUGP("ip_nat_cuseeme: expected outgoing client %u.%u.%u.%u:%u, but got %u.%u.%u.%u:%u\n", + NIPQUAD(ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.ip), + ntohs(ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.u.udp.port), + NIPQUAD(cu_head->addr), + ntohs(cu_head->port)); +} + +/* process packet from reflector->client, possibly manipulate client IP & reflector IP in payload */ +void cuseeme_mangle_incoming(struct ip_conntrack *ct, + struct ip_nat_info *info, + enum ip_conntrack_info ctinfo, + struct sk_buff **pskb, + char *data, + unsigned int datalen) +{ + char new_port_ip[6]; + struct cu_header *cu_head = (struct cu_header *)data; + struct oc_header *oc_head = (struct oc_header *)data; + struct client_info *ci; + int i, off; + + + DEBUGP("ip_nat_cuseeme: incoming packet, ID %u, dest_family %u\n", + ntohs(cu_head->data_type), ntohs(cu_head->dest_family)); + + /* Check if we're really dealing with the client's port + IP address before rewriting */ + if((ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.ip == cu_head->dest_addr) && + (ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.u.udp.port == cu_head->dest_port)) { + DEBUGP("ip_nat_cuseeme: rewrite incoming client %u.%u.%u.%u:%u->%u.%u.%u.%u:%u at offset 2\n", + NIPQUAD(cu_head->dest_addr), + ntohs(cu_head->dest_port), + NIPQUAD(ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.ip), + ntohs(ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.u.udp.port)); + *((u_int16_t *)new_port_ip) = ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.u.udp.port; + *((u_int32_t *)(new_port_ip+2)) = ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.ip; + /* at offset 2, replace 6 bytes containing port + IP address */ + ip_nat_mangle_udp_packet(pskb, ct, ctinfo, + 2, 6, (char *)(new_port_ip), 6); + } else + DEBUGP("ip_nat_cuseeme: expected incoming client %u.%u.%u.%u:%u, but got %u.%u.%u.%u:%u\n", + NIPQUAD(ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.ip), + ntohs(ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.u.udp.port), + NIPQUAD(cu_head->dest_addr), + ntohs(cu_head->dest_port)); + + /* Check if we're really dealing with the server's port + IP address before rewriting. + In some cases, the IP address == 0.0.0.0 so we don't rewrite anything */ + if((ct->tuplehash[IP_CT_DIR_REPLY].tuple.src.ip == cu_head->addr) && + (ct->tuplehash[IP_CT_DIR_REPLY].tuple.src.u.udp.port == cu_head->port)) { + DEBUGP("in_nat_cuseeme: rewrite incoming server %u.%u.%u.%u:%u->%u.%u.%u.%u:%u at offset 10\n", + NIPQUAD(cu_head->addr), + ntohs(cu_head->port), + NIPQUAD(ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.ip), + ntohs(ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.u.udp.port)); + *((u_int16_t *)new_port_ip) = ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.u.udp.port; + *((u_int32_t *)(new_port_ip+2)) = ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.ip; + /* at offset 10, replace 6 bytes containing port + IP address */ + ip_nat_mangle_udp_packet(pskb, ct, ctinfo, + 10, 6, (char *)(new_port_ip), 6); + } else + /* Sometimes we find 0.0.0.0, sometimes an IP address - the docs say this field + is not that important so we're not logging this unless we're debugging */ + DEBUGP("ip_nat_cuseeme: no biggie, expected incoming server %u.%u.%u.%u:%u, but got %u.%u.%u.%u:%u\n", + NIPQUAD(ct->tuplehash[IP_CT_DIR_REPLY].tuple.src.ip), + ntohs(ct->tuplehash[IP_CT_DIR_REPLY].tuple.src.u.udp.port), + NIPQUAD(cu_head->addr), + ntohs(cu_head->port)); + + /* Spin through client_info structs until we find our own */ + if((ntohs(cu_head->data_type) == 101) && (datalen >= sizeof(struct oc_header))) { + DEBUGP("ip_nat_cuseeme: looping through %u client_info structs\n", oc_head->client_count); + for(i=0, off=sizeof(struct oc_header); + (i < oc_head->client_count && + off+sizeof(struct client_info) <= datalen); + i++) { + ci=(struct client_info *)(data+off); + DEBUGP("ip_nat_cuseeme: comparing %u.%u.%u.%u with %u.%u.%u.%u at offset %u\n", + NIPQUAD(ci->address), NIPQUAD(ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.ip), + (unsigned int)((void *)&(ci->address) - (void *)cu_head)); + if(ci->address == ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.ip) { + /* mangle this IP address */ + DEBUGP("ip_nat_cuseeme: changing %u.%u.%u.%u->%u.%u.%u.%u at offset %u\n", + NIPQUAD(ci->address), + NIPQUAD(ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.ip), + (unsigned int)((void *)&(ci->address) - (void *)cu_head)); + ip_nat_mangle_udp_packet(pskb, ct, ctinfo, + (unsigned int)((void *)&(ci->address) - (void *)cu_head), 4, + (char *)(&(ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.ip)), 4); + break; + } else + off+=sizeof(struct client_info); + } + } else + DEBUGP("ip_nat_cuseeme: data_type %u, datalen %u < sizeof(struct oc_header) %u\n", + ntohs(cu_head->data_type), datalen, sizeof(struct oc_header)); +} + +static unsigned int +cuseeme_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; + int dir = CTINFO2DIR(ctinfo); + unsigned int datalen = (*pskb)->len - iph->ihl * 4 - sizeof(struct udphdr); + char *data = (char *) &udph[1]; + + DEBUGP("ip_nat_cuseeme: cuseeme_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" : "???" + ); + + /* 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_cuseeme: 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(datalen < sizeof(struct cu_header)) { + /* packet too small */ + if (net_ratelimit()) + printk("ip_nat_cuseeme: payload too small (%u, should be >= %u)\n", + datalen, sizeof(struct cu_header)); + return NF_ACCEPT; + } + + + /* In the debugging output, "outgoing" is from client to server, and + "incoming" is from server to client */ + if(HOOK2MANIP(hooknum) == IP_NAT_MANIP_SRC) + cuseeme_mangle_outgoing(ct, info, ctinfo, pskb, data, datalen); + else + cuseeme_mangle_incoming(ct, info, ctinfo, pskb, data, datalen); + + return NF_ACCEPT; +} + +static struct ip_nat_helper cuseeme[MAX_PORTS]; +static char cuseeme_names[MAX_PORTS][14]; /* cuseeme-65535 */ + +static void fini(void) +{ + int i; + + for (i = 0 ; i < ports_c; i++) { + DEBUGP("ip_nat_cuseeme: unregistering helper for port %d\n", ports[i]); + ip_nat_helper_unregister(&cuseeme[i]); + } +} + +static int __init init(void) +{ + int i, ret = 0; + char *tmpname; + + if (!ports[0]) + ports[0] = CUSEEME_PORT; + + for (i = 0 ; (i < MAX_PORTS) && ports[i] ; i++) { + memset(&cuseeme[i], 0, sizeof(struct ip_nat_helper)); + + cuseeme[i].tuple.dst.protonum = IPPROTO_UDP; + cuseeme[i].tuple.dst.u.udp.port = htons(ports[i]); + cuseeme[i].mask.dst.protonum = 0xFFFF; + cuseeme[i].mask.dst.u.udp.port = 0xFFFF; + cuseeme[i].help = cuseeme_nat_help; + cuseeme[i].flags = IP_NAT_HELPER_F_STANDALONE + + IP_NAT_HELPER_F_ALWAYS; /* dunno if IP_NAT_HELPER_F_ALWAYS + is stricly needed... */ + cuseeme[i].me = THIS_MODULE; + cuseeme[i].expect = NULL; /* cuseeme_nat_expected; */ + + tmpname = &cuseeme_names[i][0]; + if (ports[i] == CUSEEME_PORT) + sprintf(tmpname, "cuseeme"); + else + sprintf(tmpname, "cuseeme-%d", ports[i]); + cuseeme[i].name = tmpname; + + DEBUGP("ip_nat_cuseeme: registering helper for port %d: name %s\n", + ports[i], cuseeme[i].name); + ret = ip_nat_helper_register(&cuseeme[i]); + + if (ret) { + printk("ip_nat_cuseeme: 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-rc3.org/net/ipv4/netfilter/ip_nat_h323.c linux-2.6.6-rc3/net/ipv4/netfilter/ip_nat_h323.c --- linux-2.6.6-rc3.org/net/ipv4/netfilter/ip_nat_h323.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.6-rc3/net/ipv4/netfilter/ip_nat_h323.c 2004-04-29 11:18:00.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-rc3.org/net/ipv4/netfilter/ip_nat_mms.c linux-2.6.6-rc3/net/ipv4/netfilter/ip_nat_mms.c --- linux-2.6.6-rc3.org/net/ipv4/netfilter/ip_nat_mms.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.6-rc3/net/ipv4/netfilter/ip_nat_mms.c 2004-04-29 11:19:01.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-rc3.org/net/ipv4/netfilter/ip_nat_quake3.c linux-2.6.6-rc3/net/ipv4/netfilter/ip_nat_quake3.c --- linux-2.6.6-rc3.org/net/ipv4/netfilter/ip_nat_quake3.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.6-rc3/net/ipv4/netfilter/ip_nat_quake3.c 2004-04-29 11:19:53.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-rc3.org/net/ipv4/netfilter/ip_nat_rtsp.c linux-2.6.6-rc3/net/ipv4/netfilter/ip_nat_rtsp.c --- linux-2.6.6-rc3.org/net/ipv4/netfilter/ip_nat_rtsp.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.6-rc3/net/ipv4/netfilter/ip_nat_rtsp.c 2004-04-29 11:20:13.000000000 +0200 @@ -0,0 +1,621 @@ +/* + * RTSP extension for TCP NAT alteration + * (C) 2003 by Tom Marshall + * based on ip_nat_irc.c + * + * 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_rtsp.o ports=port1,port2,...port + * stunaddr=
+ * destaction=[auto|strip|none] + * + * If no ports are specified, the default will be port 554 only. + * + * stunaddr specifies the address used to detect that a client is using STUN. + * If this address is seen in the destination parameter, it is assumed that + * the client has already punched a UDP hole in the firewall, so we don't + * mangle the client_port. If none is specified, it is autodetected. It + * only needs to be set if you have multiple levels of NAT. It should be + * set to the external address that the STUN clients detect. Note that in + * this case, it will not be possible for clients to use UDP with servers + * between the NATs. + * + * If no destaction is specified, auto is used. + * destaction=auto: strip destination parameter if it is not stunaddr. + * destaction=strip: always strip destination parameter (not recommended). + * destaction=none: do not touch destination parameter (not recommended). + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#define NF_NEED_STRNCASECMP +#define NF_NEED_STRTOU16 +#include +#define NF_NEED_MIME_NEXTLINE +#include + +#define INFOP(fmt, args...) printk(KERN_INFO "%s: %s: " fmt, __FILE__, __FUNCTION__, ## args) +#ifdef IP_NF_RTSP_DEBUG +#define DEBUGP(fmt, args...) printk(KERN_DEBUG "%s: %s: " fmt, __FILE__, __FUNCTION__, ## args) +#else +#define DEBUGP(fmt, args...) +#endif + +#define MAX_PORTS 8 +#define DSTACT_AUTO 0 +#define DSTACT_STRIP 1 +#define DSTACT_NONE 2 + +static int ports[MAX_PORTS]; +static char* stunaddr = NULL; +static char* destaction = NULL; + +static int num_ports = 0; +static u_int32_t extip = 0; +static int dstact = 0; + +MODULE_AUTHOR("Tom Marshall "); +MODULE_DESCRIPTION("RTSP network address translation module"); +MODULE_LICENSE("GPL"); +#ifdef MODULE_PARM +MODULE_PARM(ports, "1-" __MODULE_STRING(MAX_PORTS) "i"); +MODULE_PARM_DESC(ports, "port numbers of RTSP servers"); +MODULE_PARM(stunaddr, "s"); +MODULE_PARM_DESC(stunaddr, "Address for detecting STUN"); +MODULE_PARM(destaction, "s"); +MODULE_PARM_DESC(destaction, "Action for destination parameter (auto/strip/none)"); +#endif + +/* protects rtsp part of conntracks */ +DECLARE_LOCK_EXTERN(ip_rtsp_lock); + +#define SKIP_WSPACE(ptr,len,off) while(off < len && isspace(*(ptr+off))) { off++; } + +/*** helper functions ***/ + +static void +get_skb_tcpdata(struct sk_buff* skb, char** pptcpdata, uint* ptcpdatalen) +{ + struct iphdr* iph = (struct iphdr*)skb->nh.iph; + struct tcphdr* tcph = (struct tcphdr*)((char*)iph + iph->ihl*4); + + *pptcpdata = (char*)tcph + tcph->doff*4; + *ptcpdatalen = ((char*)skb->h.raw + skb->len) - *pptcpdata; +} + +/*** nat functions ***/ + +/* + * Mangle the "Transport:" header: + * - Replace all occurences of "client_port=" + * - Handle destination parameter + * + * In: + * ct, ctinfo = conntrack context + * pskb = packet + * tranoff = Transport header offset from TCP data + * tranlen = Transport header length (incl. CRLF) + * rport_lo = replacement low port (host endian) + * rport_hi = replacement high port (host endian) + * + * Returns packet size difference. + * + * Assumes that a complete transport header is present, ending with CR or LF + */ +static int +rtsp_mangle_tran(struct ip_conntrack* ct, enum ip_conntrack_info ctinfo, + struct ip_conntrack_expect* exp, + struct sk_buff** pskb, uint tranoff, uint tranlen) +{ + char* ptcp; + uint tcplen; + char* ptran; + char rbuf1[16]; /* Replacement buffer (one port) */ + uint rbuf1len; /* Replacement len (one port) */ + char rbufa[16]; /* Replacement buffer (all ports) */ + uint rbufalen; /* Replacement len (all ports) */ + u_int32_t newip; + u_int16_t loport, hiport; + uint off = 0; + uint diff; /* Number of bytes we removed */ + + struct ip_ct_rtsp_expect* prtspexp = &exp->help.exp_rtsp_info; + struct ip_conntrack_tuple t; + + char szextaddr[15+1]; + uint extaddrlen; + int is_stun; + + get_skb_tcpdata(*pskb, &ptcp, &tcplen); + ptran = ptcp+tranoff; + + if (tranoff+tranlen > tcplen || tcplen-tranoff < tranlen || + tranlen < 10 || !iseol(ptran[tranlen-1]) || + nf_strncasecmp(ptran, "Transport:", 10) != 0) + { + INFOP("sanity check failed\n"); + return 0; + } + off += 10; + SKIP_WSPACE(ptcp+tranoff, tranlen, off); + + newip = ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.ip; + t = exp->tuple; + t.dst.ip = newip; + + extaddrlen = extip ? sprintf(szextaddr, "%u.%u.%u.%u", NIPQUAD(extip)) + : sprintf(szextaddr, "%u.%u.%u.%u", NIPQUAD(newip)); + DEBUGP("stunaddr=%s (%s)\n", szextaddr, (extip?"forced":"auto")); + + rbuf1len = rbufalen = 0; + switch (prtspexp->pbtype) + { + case pb_single: + for (loport = prtspexp->loport; loport != 0; loport++) /* XXX: improper wrap? */ + { + t.dst.u.udp.port = htons(loport); + if (ip_conntrack_change_expect(exp, &t) == 0) + { + DEBUGP("using port %hu\n", loport); + break; + } + } + if (loport != 0) + { + rbuf1len = sprintf(rbuf1, "%hu", loport); + rbufalen = sprintf(rbufa, "%hu", loport); + } + break; + case pb_range: + for (loport = prtspexp->loport; loport != 0; loport += 2) /* XXX: improper wrap? */ + { + t.dst.u.udp.port = htons(loport); + if (ip_conntrack_change_expect(exp, &t) == 0) + { + hiport = loport + ~exp->mask.dst.u.udp.port; + DEBUGP("using ports %hu-%hu\n", loport, hiport); + break; + } + } + if (loport != 0) + { + rbuf1len = sprintf(rbuf1, "%hu", loport); + rbufalen = sprintf(rbufa, "%hu-%hu", loport, loport+1); + } + break; + case pb_discon: + for (loport = prtspexp->loport; loport != 0; loport++) /* XXX: improper wrap? */ + { + t.dst.u.udp.port = htons(loport); + if (ip_conntrack_change_expect(exp, &t) == 0) + { + DEBUGP("using port %hu (1 of 2)\n", loport); + break; + } + } + for (hiport = prtspexp->hiport; hiport != 0; hiport++) /* XXX: improper wrap? */ + { + t.dst.u.udp.port = htons(hiport); + if (ip_conntrack_change_expect(exp, &t) == 0) + { + DEBUGP("using port %hu (2 of 2)\n", hiport); + break; + } + } + if (loport != 0 && hiport != 0) + { + rbuf1len = sprintf(rbuf1, "%hu", loport); + if (hiport == loport+1) + { + rbufalen = sprintf(rbufa, "%hu-%hu", loport, hiport); + } + else + { + rbufalen = sprintf(rbufa, "%hu/%hu", loport, hiport); + } + } + break; + } + + if (rbuf1len == 0) + { + return 0; /* cannot get replacement port(s) */ + } + + /* Transport: tran;field;field=val,tran;field;field=val,... */ + while (off < tranlen) + { + uint saveoff; + const char* pparamend; + uint nextparamoff; + + pparamend = memchr(ptran+off, ',', tranlen-off); + pparamend = (pparamend == NULL) ? ptran+tranlen : pparamend+1; + nextparamoff = pparamend-ptcp; + + /* + * We pass over each param twice. On the first pass, we look for a + * destination= field. It is handled by the security policy. If it + * is present, allowed, and equal to our external address, we assume + * that STUN is being used and we leave the client_port= field alone. + */ + is_stun = 0; + saveoff = off; + while (off < nextparamoff) + { + const char* pfieldend; + uint nextfieldoff; + + pfieldend = memchr(ptran+off, ';', nextparamoff-off); + nextfieldoff = (pfieldend == NULL) ? nextparamoff : pfieldend-ptran+1; + + if (dstact != DSTACT_NONE && strncmp(ptran+off, "destination=", 12) == 0) + { + if (strncmp(ptran+off+12, szextaddr, extaddrlen) == 0) + { + is_stun = 1; + } + if (dstact == DSTACT_STRIP || (dstact == DSTACT_AUTO && !is_stun)) + { + diff = nextfieldoff-off; + if (!ip_nat_mangle_tcp_packet(pskb, ct, ctinfo, + off, diff, NULL, 0)) + { + /* mangle failed, all we can do is bail */ + return 0; + } + get_skb_tcpdata(*pskb, &ptcp, &tcplen); + ptran = ptcp+tranoff; + tranlen -= diff; + nextparamoff -= diff; + nextfieldoff -= diff; + } + } + + off = nextfieldoff; + } + if (is_stun) + { + continue; + } + off = saveoff; + while (off < nextparamoff) + { + const char* pfieldend; + uint nextfieldoff; + + pfieldend = memchr(ptran+off, ';', nextparamoff-off); + nextfieldoff = (pfieldend == NULL) ? nextparamoff : pfieldend-ptran+1; + + if (strncmp(ptran+off, "client_port=", 12) == 0) + { + u_int16_t port; + uint numlen; + uint origoff; + uint origlen; + char* rbuf = rbuf1; + uint rbuflen = rbuf1len; + + off += 12; + origoff = (ptran-ptcp)+off; + origlen = 0; + numlen = nf_strtou16(ptran+off, &port); + off += numlen; + origlen += numlen; + if (port != prtspexp->loport) + { + DEBUGP("multiple ports found, port %hu ignored\n", port); + } + else + { + if (ptran[off] == '-' || ptran[off] == '/') + { + off++; + origlen++; + numlen = nf_strtou16(ptran+off, &port); + off += numlen; + origlen += numlen; + rbuf = rbufa; + rbuflen = rbufalen; + } + + /* + * note we cannot just memcpy() if the sizes are the same. + * the mangle function does skb resizing, checks for a + * cloned skb, and updates the checksums. + * + * parameter 4 below is offset from start of tcp data. + */ + diff = origlen-rbuflen; + if (!ip_nat_mangle_tcp_packet(pskb, ct, ctinfo, + origoff, origlen, rbuf, rbuflen)) + { + /* mangle failed, all we can do is bail */ + return 0; + } + get_skb_tcpdata(*pskb, &ptcp, &tcplen); + ptran = ptcp+tranoff; + tranlen -= diff; + nextparamoff -= diff; + nextfieldoff -= diff; + } + } + + off = nextfieldoff; + } + + off = nextparamoff; + } + + return 1; +} + +static unsigned int +expected(struct sk_buff **pskb, uint 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)))); + + newdstip = master->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.ip; + newsrcip = ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.ip; + newip = (HOOK2MANIP(hooknum) == IP_NAT_MANIP_SRC) ? newsrcip : newdstip; + + DEBUGP("newsrcip=%u.%u.%u.%u, newdstip=%u.%u.%u.%u, newip=%u.%u.%u.%u\n", + NIPQUAD(newsrcip), NIPQUAD(newdstip), 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 uint +help_out(struct ip_conntrack* ct, enum ip_conntrack_info ctinfo, + struct ip_conntrack_expect* exp, struct sk_buff** pskb) +{ + char* ptcp; + uint tcplen; + uint hdrsoff; + uint hdrslen; + uint lineoff; + uint linelen; + uint off; + + struct iphdr* iph = (struct iphdr*)(*pskb)->nh.iph; + struct tcphdr* tcph = (struct tcphdr*)((void*)iph + iph->ihl*4); + + struct ip_ct_rtsp_expect* prtspexp = &exp->help.exp_rtsp_info; + + get_skb_tcpdata(*pskb, &ptcp, &tcplen); + + hdrsoff = exp->seq - ntohl(tcph->seq); + hdrslen = prtspexp->len; + off = hdrsoff; + + while (nf_mime_nextline(ptcp, hdrsoff+hdrslen, &off, &lineoff, &linelen)) + { + if (linelen == 0) + { + break; + } + if (off > hdrsoff+hdrslen) + { + INFOP("!! overrun !!"); + break; + } + DEBUGP("hdr: len=%u, %.*s", linelen, (int)linelen, ptcp+lineoff); + + if (nf_strncasecmp(ptcp+lineoff, "Transport:", 10) == 0) + { + uint oldtcplen = tcplen; + if (!rtsp_mangle_tran(ct, ctinfo, exp, pskb, lineoff, linelen)) + { + break; + } + get_skb_tcpdata(*pskb, &ptcp, &tcplen); + hdrslen -= (oldtcplen-tcplen); + off -= (oldtcplen-tcplen); + lineoff -= (oldtcplen-tcplen); + linelen -= (oldtcplen-tcplen); + DEBUGP("rep: len=%u, %.*s", linelen, (int)linelen, ptcp+lineoff); + } + } + + return NF_ACCEPT; +} + +static uint +help_in(struct ip_conntrack* ct, enum ip_conntrack_info ctinfo, + struct ip_conntrack_expect* exp, struct sk_buff** pskb) +{ + /* XXX: unmangle */ + return NF_ACCEPT; +} + +static uint +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 = (struct iphdr*)(*pskb)->nh.iph; + struct tcphdr* tcph = (struct tcphdr*)((char*)iph + iph->ihl * 4); + uint datalen; + int dir; + struct ip_ct_rtsp_expect* ct_rtsp_info; + int rc = NF_ACCEPT; + + if (ct == NULL || exp == NULL || info == NULL || pskb == NULL) + { + DEBUGP("!! null ptr (%p,%p,%p,%p) !!\n", ct, exp, info, pskb); + return NF_ACCEPT; + } + + ct_rtsp_info = &exp->help.exp_rtsp_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("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("got beyond not touching\n"); + + datalen = (*pskb)->len - iph->ihl * 4 - tcph->doff * 4; + + LOCK_BH(&ip_rtsp_lock); + /* Ensure the packet contains all of the marked data */ + if (!between(exp->seq + ct_rtsp_info->len, + ntohl(tcph->seq), ntohl(tcph->seq) + datalen)) + { + /* Partial retransmission? Probably a hacker. */ + if (net_ratelimit()) + { + INFOP("partial packet %u/%u in %u/%u\n", + exp->seq, ct_rtsp_info->len, ntohl(tcph->seq), ntohl(tcph->seq) + datalen); + } + UNLOCK_BH(&ip_rtsp_lock); + return NF_DROP; + } + + switch (dir) + { + case IP_CT_DIR_ORIGINAL: + rc = help_out(ct, ctinfo, exp, pskb); + break; + case IP_CT_DIR_REPLY: + rc = help_in(ct, ctinfo, exp, pskb); + break; + } + UNLOCK_BH(&ip_rtsp_lock); + + return rc; +} + +static struct ip_nat_helper ip_nat_rtsp_helpers[MAX_PORTS]; +static char rtsp_names[MAX_PORTS][10]; + +/* This function is intentionally _NOT_ defined as __exit */ +static void +fini(void) +{ + int i; + + for (i = 0; i < num_ports; i++) + { + DEBUGP("unregistering helper for port %d\n", ports[i]); + ip_nat_helper_unregister(&ip_nat_rtsp_helpers[i]); + } +} + +static int __init +init(void) +{ + int ret = 0; + int i; + struct ip_nat_helper* hlpr; + char* tmpname; + + printk("ip_nat_rtsp v" IP_NF_RTSP_VERSION " loading\n"); + + if (ports[0] == 0) + { + ports[0] = RTSP_PORT; + } + + for (i = 0; (i < MAX_PORTS) && ports[i] != 0; i++) + { + hlpr = &ip_nat_rtsp_helpers[i]; + memset(hlpr, 0, sizeof(struct ip_nat_helper)); + + hlpr->tuple.dst.protonum = IPPROTO_TCP; + hlpr->tuple.src.u.tcp.port = htons(ports[i]); + hlpr->mask.src.u.tcp.port = 0xFFFF; + hlpr->mask.dst.protonum = 0xFFFF; + hlpr->help = help; + hlpr->flags = 0; + hlpr->me = THIS_MODULE; + hlpr->expect = expected; + + tmpname = &rtsp_names[i][0]; + if (ports[i] == RTSP_PORT) + { + sprintf(tmpname, "rtsp"); + } + else + { + sprintf(tmpname, "rtsp-%d", i); + } + hlpr->name = tmpname; + + DEBUGP("registering helper for port %d: name %s\n", ports[i], hlpr->name); + ret = ip_nat_helper_register(hlpr); + + if (ret) + { + printk("ip_nat_rtsp: error registering helper for port %d\n", ports[i]); + fini(); + return 1; + } + num_ports++; + } + if (stunaddr != NULL) + { + extip = in_aton(stunaddr); + } + if (destaction != NULL) + { + if (strcmp(destaction, "auto") == 0) + { + dstact = DSTACT_AUTO; + } + if (strcmp(destaction, "strip") == 0) + { + dstact = DSTACT_STRIP; + } + if (strcmp(destaction, "none") == 0) + { + dstact = DSTACT_NONE; + } + } + return ret; +} + +module_init(init); +module_exit(fini); diff -Nur --exclude '*.orig' linux-2.6.6-rc3.org/net/ipv4/netfilter/ip_nat_rule.c linux-2.6.6-rc3/net/ipv4/netfilter/ip_nat_rule.c --- linux-2.6.6-rc3.org/net/ipv4/netfilter/ip_nat_rule.c 2004-04-28 03:35:44.000000000 +0200 +++ linux-2.6.6-rc3/net/ipv4/netfilter/ip_nat_rule.c 2004-04-29 11:17:15.000000000 +0200 @@ -75,7 +75,7 @@ 0, sizeof(struct ipt_entry), sizeof(struct ipt_standard), - 0, { 0, 0 }, { } }, + 0, NULL, 0, { 0, 0 }, { } }, { { { { IPT_ALIGN(sizeof(struct ipt_standard_target)), "" } }, { } }, -NF_ACCEPT - 1 } }, /* POST_ROUTING */ @@ -83,7 +83,7 @@ 0, sizeof(struct ipt_entry), sizeof(struct ipt_standard), - 0, { 0, 0 }, { } }, + 0, NULL, 0, { 0, 0 }, { } }, { { { { IPT_ALIGN(sizeof(struct ipt_standard_target)), "" } }, { } }, -NF_ACCEPT - 1 } }, /* LOCAL_OUT */ @@ -91,7 +91,7 @@ 0, sizeof(struct ipt_entry), sizeof(struct ipt_standard), - 0, { 0, 0 }, { } }, + 0, NULL, 0, { 0, 0 }, { } }, { { { { IPT_ALIGN(sizeof(struct ipt_standard_target)), "" } }, { } }, -NF_ACCEPT - 1 } } }, @@ -100,7 +100,7 @@ 0, sizeof(struct ipt_entry), sizeof(struct ipt_error), - 0, { 0, 0 }, { } }, + 0, NULL, 0, { 0, 0 }, { } }, { { { { IPT_ALIGN(sizeof(struct ipt_error_target)), IPT_ERROR_TARGET } }, { } }, "ERROR" diff -Nur --exclude '*.orig' linux-2.6.6-rc3.org/net/ipv4/netfilter/ip_nat_standalone.c linux-2.6.6-rc3/net/ipv4/netfilter/ip_nat_standalone.c --- linux-2.6.6-rc3.org/net/ipv4/netfilter/ip_nat_standalone.c 2004-04-28 03:36:29.000000000 +0200 +++ linux-2.6.6-rc3/net/ipv4/netfilter/ip_nat_standalone.c 2004-04-29 11:18:04.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-rc3.org/net/ipv4/netfilter/ip_nat_talk.c linux-2.6.6-rc3/net/ipv4/netfilter/ip_nat_talk.c --- linux-2.6.6-rc3.org/net/ipv4/netfilter/ip_nat_talk.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.6-rc3/net/ipv4/netfilter/ip_nat_talk.c 2004-04-29 11:21:43.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-rc3.org/net/ipv4/netfilter/ip_tables.c linux-2.6.6-rc3/net/ipv4/netfilter/ip_tables.c --- linux-2.6.6-rc3.org/net/ipv4/netfilter/ip_tables.c 2004-04-29 11:24:39.000000000 +0200 +++ linux-2.6.6-rc3/net/ipv4/netfilter/ip_tables.c 2004-04-29 11:17:15.000000000 +0200 @@ -29,6 +29,14 @@ #include +static const char *hooknames[] = { + [NF_IP_PRE_ROUTING] "PREROUTING", + [NF_IP_LOCAL_IN] "INPUT", + [NF_IP_FORWARD] "FORWARD", + [NF_IP_LOCAL_OUT] "OUTPUT", + [NF_IP_POST_ROUTING] "POSTROUTING", +}; + MODULE_LICENSE("GPL"); MODULE_AUTHOR("Netfilter Core Team "); MODULE_DESCRIPTION("IPv4 packet filter"); @@ -326,6 +334,12 @@ t = ipt_get_target(e); IP_NF_ASSERT(t->u.kernel.target); + + /* The packet traced and the rule isn't an unconditional return/END. */ + if (((*pskb)->nfcache & NFC_TRACE) && e->rulenum) { + nf_log_packet(PF_INET, hook, *pskb, in, out, "TRACE: %s/%s/%u ", + table->name, e->chainname, e->rulenum); + } /* Standard target? */ if (!t->u.kernel.target->target) { int v; @@ -352,7 +366,6 @@ /* set back pointer to next entry */ back = next; } - e = get_entry(table_base, v); } else { /* Targets which reenter must return @@ -478,6 +491,29 @@ return find_inlist_lock(&ipt_target, name, "ipt_", error, mutex); } +static inline int +find_error_target(struct ipt_entry *s, + struct ipt_entry *e, + char **chainname) +{ + struct ipt_entry_target *t; + static struct ipt_entry *found = NULL; + + if (s == e) { + if (!found) + return 0; + t = ipt_get_target(found); + if (strcmp(t->u.user.name, + IPT_ERROR_TARGET) == 0) { + *chainname = t->data; + return 1; + } + } else + found = s; + + return 0; +} + /* All zeroes == unconditional rule. */ static inline int unconditional(const struct ipt_ip *ip) @@ -497,6 +533,8 @@ mark_source_chains(struct ipt_table_info *newinfo, unsigned int valid_hooks) { unsigned int hook; + char *chainname = NULL; + u_int32_t rulenum; /* No recursion; use packet counter to save back ptrs (reset to 0 as we leave), and comefrom to save source hook bitmask */ @@ -510,6 +548,8 @@ /* Set initial back pointer. */ e->counters.pcnt = pos; + rulenum = 1; + chainname = (char *) hooknames[hook]; for (;;) { struct ipt_standard_target *t @@ -522,6 +562,8 @@ } e->comefrom |= ((1 << hook) | (1 << NF_IP_NUMHOOKS)); + e->rulenum = rulenum++; + e->chainname = chainname; /* Unconditional return/END. */ if (e->target_offset == sizeof(struct ipt_entry) @@ -531,6 +573,10 @@ && unconditional(&e->ip)) { unsigned int oldpos, size; + /* Set unconditional rulenum to zero. */ + e->rulenum = 0; + e->counters.bcnt = 0; + /* Return: backtrack through the last big jump. */ do { @@ -556,6 +602,11 @@ (newinfo->entries + pos); } while (oldpos == pos + e->next_offset); + /* Restore chainname, rulenum. */ + chainname = e->chainname; + rulenum = e->counters.bcnt; + e->counters.bcnt = 0; + /* Move along one */ size = e->next_offset; e = (struct ipt_entry *) @@ -571,6 +622,17 @@ /* This a jump; chase it. */ duprintf("Jump rule %u -> %u\n", pos, newpos); + e->counters.bcnt = rulenum++; + rulenum = 1; + e = (struct ipt_entry *) + (newinfo->entries + newpos); + if (IPT_ENTRY_ITERATE(newinfo->entries, + newinfo->size, + find_error_target, + e, &chainname) == 0) { + printk("ip_tables: table screwed up!\n"); + return 0; + } } else { /* ... this is a fallthru */ newpos = pos + e->next_offset; diff -Nur --exclude '*.orig' linux-2.6.6-rc3.org/net/ipv4/netfilter/ipt_CONNMARK.c linux-2.6.6-rc3/net/ipv4/netfilter/ipt_CONNMARK.c --- linux-2.6.6-rc3.org/net/ipv4/netfilter/ipt_CONNMARK.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.6-rc3/net/ipv4/netfilter/ipt_CONNMARK.c 2004-04-29 11:10:59.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-rc3.org/net/ipv4/netfilter/ipt_IPMARK.c linux-2.6.6-rc3/net/ipv4/netfilter/ipt_IPMARK.c --- linux-2.6.6-rc3.org/net/ipv4/netfilter/ipt_IPMARK.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.6-rc3/net/ipv4/netfilter/ipt_IPMARK.c 2004-04-29 11:17:03.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-rc3.org/net/ipv4/netfilter/ipt_TARPIT.c linux-2.6.6-rc3/net/ipv4/netfilter/ipt_TARPIT.c --- linux-2.6.6-rc3.org/net/ipv4/netfilter/ipt_TARPIT.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.6-rc3/net/ipv4/netfilter/ipt_TARPIT.c 2004-04-29 11:17:08.000000000 +0200 @@ -0,0 +1,289 @@ +/* + * Kernel module to capture and hold incoming TCP connections using + * no local per-connection resources. + * + * Based on ipt_REJECT.c and offering functionality similar to + * LaBrea . + * + * Copyright (c) 2002 Aaron Hopkins + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Goal: + * - Allow incoming TCP connections to be established. + * - Passing data should result in the connection being switched to the + * persist state (0 byte window), in which the remote side stops sending + * data and asks to continue every 60 seconds. + * - Attempts to shut down the connection should be ignored completely, so + * the remote side ends up having to time it out. + * + * This means: + * - Reply to TCP SYN,!ACK,!RST,!FIN with SYN-ACK, window 5 bytes + * - Reply to TCP SYN,ACK,!RST,!FIN with RST to prevent spoofing + * - Reply to TCP !SYN,!RST,!FIN with ACK, window 0 bytes, rate-limited + */ + +#include +#include +#include +#include +#include +#include +#include +struct in_device; +#include +#include +#include + +#if 0 +#define DEBUGP printk +#else +#define DEBUGP(format, args...) +#endif + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Aaron Hopkins "); + +/* Stolen from ip_finish_output2 */ +static int ip_direct_send(struct sk_buff *skb) +{ + struct dst_entry *dst = skb->dst; + struct hh_cache *hh = dst->hh; + + if (hh) { + read_lock_bh(&hh->hh_lock); + memcpy(skb->data - 16, hh->hh_data, 16); + read_unlock_bh(&hh->hh_lock); + skb_push(skb, hh->hh_len); + return hh->hh_output(skb); + } else if (dst->neighbour) + return dst->neighbour->output(skb); + + if (net_ratelimit()) + printk(KERN_DEBUG "TARPIT ip_direct_send: no header cache and no neighbor!\n"); + kfree_skb(skb); + return -EINVAL; +} + + +/* Send reply */ +static void tarpit_tcp(struct sk_buff *oskb,struct rtable *ort,int local) +{ + struct sk_buff *nskb; + struct rtable *nrt; + struct tcphdr *otcph, *ntcph; + unsigned int otcplen; + u_int16_t tmp; + 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, + const struct net_device *in, + const struct net_device *out, + unsigned int hooknum, + const void *targinfo, + void *userinfo) +{ + struct sk_buff *skb = *pskb; + struct rtable *rt = (struct rtable*)skb->dst; + + /* Do we have an input route cache entry? */ + if (!rt) + return NF_DROP; + + /* No replies to physical multicast/broadcast */ + if (skb->pkt_type != PACKET_HOST && skb->pkt_type != PACKET_OTHERHOST) + return NF_DROP; + + /* Now check at the protocol level */ + if (rt->rt_flags&(RTCF_BROADCAST|RTCF_MULTICAST)) + return NF_DROP; + + /* Our naive response construction doesn't deal with IP + options, and probably shouldn't try. */ + if (skb->nh.iph->ihl*4 != sizeof(struct iphdr)) + return NF_DROP; + + /* We aren't interested in fragments */ + if (skb->nh.iph->frag_off & htons(IP_OFFSET)) + return NF_DROP; + + tarpit_tcp(skb,rt,hooknum == NF_IP_LOCAL_IN); + + return NF_DROP; +} + + +static int check(const char *tablename, + const struct ipt_entry *e, + void *targinfo, + unsigned int targinfosize, + unsigned int hook_mask) +{ + /* Only allow these for input/forward packet filtering. */ + if (strcmp(tablename, "filter") != 0) { + DEBUGP("TARPIT: bad table %s'.\n", tablename); + return 0; + } + if ((hook_mask & ~((1 << NF_IP_LOCAL_IN) + | (1 << NF_IP_FORWARD))) != 0) { + DEBUGP("TARPIT: bad hook mask %X\n", hook_mask); + return 0; + } + + /* Must specify that it's a TCP packet */ + if (e->ip.proto != IPPROTO_TCP || (e->ip.invflags & IPT_INV_PROTO)) { + DEBUGP("TARPIT: not valid for non-tcp\n"); + return 0; + } + + return 1; +} + +static struct ipt_target ipt_tarpit_reg = { + .name = "TARPIT", + .target = tarpit, + .checkentry = check, + .me = THIS_MODULE +}; + +static int __init init(void) +{ + return ipt_register_target(&ipt_tarpit_reg); +} + +static void __exit fini(void) +{ + ipt_unregister_target(&ipt_tarpit_reg); +} + +module_init(init); +module_exit(fini); diff -Nur --exclude '*.orig' linux-2.6.6-rc3.org/net/ipv4/netfilter/ipt_TRACE.c linux-2.6.6-rc3/net/ipv4/netfilter/ipt_TRACE.c --- linux-2.6.6-rc3.org/net/ipv4/netfilter/ipt_TRACE.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.6-rc3/net/ipv4/netfilter/ipt_TRACE.c 2004-04-29 11:17:15.000000000 +0200 @@ -0,0 +1,64 @@ +/* This is a module which is used for setting + * the NFC_TRACE flag in the nfcache field of an skb. + */ +#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) +{ + (*pskb)->nfcache |= NFC_TRACE; + 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 != 0) { + printk(KERN_WARNING "TRACE: targinfosize %u != 0\n", + targinfosize); + return 0; + } + + if (strcmp(tablename, "raw") != 0) { + printk(KERN_WARNING "TRACE: can only be called from \"raw\" table, not \"%s\"\n", tablename); + return 0; + } + + return 1; +} + +static struct ipt_target ipt_trace_reg = { + .name = "TRACE", + .target = target, + .checkentry = checkentry, + .me = THIS_MODULE +}; + +static int __init init(void) +{ + if (ipt_register_target(&ipt_trace_reg)) + return -EINVAL; + + return 0; +} + +static void __exit fini(void) +{ + ipt_unregister_target(&ipt_trace_reg); +} + +module_init(init); +module_exit(fini); +MODULE_LICENSE("GPL"); diff -Nur --exclude '*.orig' linux-2.6.6-rc3.org/net/ipv4/netfilter/ipt_XOR.c linux-2.6.6-rc3/net/ipv4/netfilter/ipt_XOR.c --- linux-2.6.6-rc3.org/net/ipv4/netfilter/ipt_XOR.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.6-rc3/net/ipv4/netfilter/ipt_XOR.c 2004-04-29 11:17:18.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-rc3.org/net/ipv4/netfilter/ipt_addrtype.c linux-2.6.6-rc3/net/ipv4/netfilter/ipt_addrtype.c --- linux-2.6.6-rc3.org/net/ipv4/netfilter/ipt_addrtype.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.6-rc3/net/ipv4/netfilter/ipt_addrtype.c 2004-04-29 11:17:23.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-rc3.org/net/ipv4/netfilter/ipt_connmark.c linux-2.6.6-rc3/net/ipv4/netfilter/ipt_connmark.c --- linux-2.6.6-rc3.org/net/ipv4/netfilter/ipt_connmark.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.6-rc3/net/ipv4/netfilter/ipt_connmark.c 2004-04-29 11:10:59.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-rc3.org/net/ipv4/netfilter/ipt_helper.c linux-2.6.6-rc3/net/ipv4/netfilter/ipt_helper.c --- linux-2.6.6-rc3.org/net/ipv4/netfilter/ipt_helper.c 2004-04-29 11:24:39.000000000 +0200 +++ linux-2.6.6-rc3/net/ipv4/netfilter/ipt_helper.c 2004-04-29 11:18:37.000000000 +0200 @@ -71,8 +71,11 @@ DEBUGP("master's name = %s , info->name = %s\n", exp->expectant->helper->name, info->name); - ret ^= !strncmp(exp->expectant->helper->name, info->name, - strlen(exp->expectant->helper->name)); + if (info->name[0] == '\0') + ret ^= 1; + else + ret ^= !strncmp(exp->expectant->helper->name, info->name, + strlen(exp->expectant->helper->name)); out_unlock: READ_UNLOCK(&ip_conntrack_lock); return ret; @@ -92,10 +95,6 @@ if (matchsize != IPT_ALIGN(sizeof(struct ipt_helper_info))) return 0; - /* verify that we actually should match anything */ - if ( strlen(info->name) == 0 ) - return 0; - return 1; } diff -Nur --exclude '*.orig' linux-2.6.6-rc3.org/net/ipv4/netfilter/ipt_owner.c linux-2.6.6-rc3/net/ipv4/netfilter/ipt_owner.c --- linux-2.6.6-rc3.org/net/ipv4/netfilter/ipt_owner.c 2004-04-28 03:36:37.000000000 +0200 +++ linux-2.6.6-rc3/net/ipv4/netfilter/ipt_owner.c 2004-04-29 11:19:32.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-rc3.org/net/ipv4/netfilter/ipt_policy.c linux-2.6.6-rc3/net/ipv4/netfilter/ipt_policy.c --- linux-2.6.6-rc3.org/net/ipv4/netfilter/ipt_policy.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.6-rc3/net/ipv4/netfilter/ipt_policy.c 2004-04-29 11:19:41.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-rc3.org/net/ipv4/netfilter/ipt_rpc.c linux-2.6.6-rc3/net/ipv4/netfilter/ipt_rpc.c --- linux-2.6.6-rc3.org/net/ipv4/netfilter/ipt_rpc.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.6-rc3/net/ipv4/netfilter/ipt_rpc.c 2004-04-29 11:20:02.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-rc3.org/net/ipv4/netfilter/ipt_string.c linux-2.6.6-rc3/net/ipv4/netfilter/ipt_string.c --- linux-2.6.6-rc3.org/net/ipv4/netfilter/ipt_string.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.6-rc3/net/ipv4/netfilter/ipt_string.c 2004-04-29 11:21:38.000000000 +0200 @@ -0,0 +1,183 @@ +/* Kernel module to match a string into a packet. + * + * Copyright (C) 2000 Emmanuel Roger + * + * ChangeLog + * 24.03.2004: Eric Lauriault + * Initial 2.6 port + * 19.02.2002: Gianni Tedesco + * Fixed SMP re-entrancy problem using per-cpu data areas + * for the skip/shift tables. + * 02.05.2001: Gianni Tedesco + * Fixed kernel panic, due to overrunning boyer moore string + * tables. Also slightly tweaked heuristic for deciding what + * search algo to use. + * 27.01.2001: Gianni Tedesco + * Implemented Boyer Moore Sublinear search algorithm + * alongside the existing linear search based on memcmp(). + * Also a quick check to decide which method to use on a per + * packet basis. + */ + +#include +#include +#include +#include +#include +#include + +#include +#include + +MODULE_LICENSE("GPL"); + +struct string_per_cpu { + int skip[BM_MAX_HLEN]; + int shift[BM_MAX_HLEN]; + int len[BM_MAX_HLEN]; +}; + +static DEFINE_PER_CPU(struct string_per_cpu, bm_string_data); + + +/* Boyer Moore Sublinear string search - VERY FAST */ +char *search_sublinear (char *needle, char *haystack, int needle_len, int haystack_len) +{ + int M1, right_end, sk, sh; + int ended, j, i; + + int *skip, *shift, *len; + + /* use data suitable for this CPU */ + shift=__get_cpu_var(bm_string_data).shift; + skip=__get_cpu_var(bm_string_data).skip; + len=__get_cpu_var(bm_string_data).len; + + /* Setup skip/shift tables */ + M1 = right_end = needle_len-1; + for (i = 0; i < BM_MAX_HLEN; i++) skip[i] = needle_len; + for (i = 0; needle[i]; i++) skip[(int)needle[i]] = M1 - i; + + for (i = 1; i < needle_len; i++) { + for (j = 0; j < needle_len && needle[M1 - j] == needle[M1 - i - j]; j++); + len[i] = j; + } + + shift[0] = 1; + for (i = 1; i < needle_len; i++) shift[i] = needle_len; + for (i = M1; i > 0; i--) shift[len[i]] = i; + ended = 0; + + for (i = 0; i < needle_len; i++) { + if (len[i] == M1 - i) ended = i; + if (ended) shift[i] = ended; + } + + /* Do the search*/ + while (right_end < haystack_len) + { + for (i = 0; i < needle_len && haystack[right_end - i] == needle[M1 - i]; i++); + if (i == needle_len) { + return haystack+(right_end - M1); + } + + sk = skip[(int)haystack[right_end - i]]; + sh = shift[i]; + right_end = max(right_end - i + sk, right_end + sh); + } + + return NULL; +} + +/* Linear string search based on memcmp() */ +char *search_linear (char *needle, char *haystack, int needle_len, int haystack_len) +{ + char *k = haystack + (haystack_len-needle_len); + char *t = haystack; + + while ( t <= k ) { + if (memcmp(t, needle, needle_len) == 0) + return t; + t++; + } + + return NULL; +} + +static int +match(const struct sk_buff *skb, + const struct net_device *in, + const struct net_device *out, + const void *matchinfo, + int offset, + int *hotdrop) +{ + const struct ipt_string_info *info = matchinfo; + struct iphdr *ip = skb->nh.iph; + int hlen, nlen; + char *needle, *haystack; + proc_ipt_search search=search_linear; + + if ( !ip ) return 0; + + /* get lenghts, and validate them */ + nlen=info->len; + hlen=ntohs(ip->tot_len)-(ip->ihl*4); + if ( nlen > hlen ) return 0; + + needle=(char *)&info->string; + haystack=(char *)ip+(ip->ihl*4); + + /* The sublinear search comes in to its own + * on the larger packets */ + if ( (hlen>IPT_STRING_HAYSTACK_THRESH) && + (nlen>IPT_STRING_NEEDLE_THRESH) ) { + if ( hlen < BM_MAX_HLEN ) { + search=search_sublinear; + }else{ + if (net_ratelimit()) + printk(KERN_INFO "ipt_string: Packet too big " + "to attempt sublinear string search " + "(%d bytes)\n", hlen ); + } + } + + return ((search(needle, haystack, nlen, hlen)!=NULL) ^ info->invert); +} + +static int +checkentry(const char *tablename, + const struct ipt_ip *ip, + void *matchinfo, + unsigned int matchsize, + unsigned int hook_mask) +{ + + if (matchsize != IPT_ALIGN(sizeof(struct ipt_string_info))) + return 0; + + return 1; +} + +static struct ipt_match string_match = { + .name = "string", + .match = &match, + .checkentry = &checkentry, + .me = THIS_MODULE +}; + + +static int __init init(void) +{ + return ipt_register_match(&string_match); +} + +static void __exit fini(void) +{ + ipt_unregister_match(&string_match); +} + +module_init(init); +module_exit(fini); + + diff -Nur --exclude '*.orig' linux-2.6.6-rc3.org/net/ipv4/netfilter/ipt_unclean.c linux-2.6.6-rc3/net/ipv4/netfilter/ipt_unclean.c --- linux-2.6.6-rc3.org/net/ipv4/netfilter/ipt_unclean.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.6-rc3/net/ipv4/netfilter/ipt_unclean.c 2004-04-29 11:21:33.000000000 +0200 @@ -0,0 +1,604 @@ +/* Kernel module to match suspect packets. */ +#include +#include +#include +#include +#include +#include +#include + +#include + +#define limpk(format, args...) \ +do { \ + if (net_ratelimit()) \ + printk("ipt_unclean: %s" format, \ + embedded ? "(embedded packet) " : "" , ## args); \ +} while(0) + +enum icmp_error_status +{ + ICMP_MAY_BE_ERROR, + ICMP_IS_ERROR, + ICMP_NOT_ERROR +}; + +struct icmp_info +{ + size_t min_len, max_len; + enum icmp_error_status err; + u_int8_t min_code, max_code; +}; + +static int +check_ip(struct iphdr *iph, size_t length, int embedded); + +/* ICMP-specific checks. */ +static int +check_icmp(const struct icmphdr *icmph, + u_int16_t datalen, + unsigned int offset, + int more_frags, + int embedded) +{ + static struct icmp_info info[] + = { [ICMP_ECHOREPLY] + = { 8, 65536, ICMP_NOT_ERROR, 0, 0 }, + [ICMP_DEST_UNREACH] + = { 8 + 28, 65536, ICMP_IS_ERROR, 0, 15 }, + [ICMP_SOURCE_QUENCH] + = { 8 + 28, 65536, ICMP_IS_ERROR, 0, 0 }, + [ICMP_REDIRECT] + = { 8 + 28, 65536, ICMP_IS_ERROR, 0, 3 }, + [ICMP_ECHO] + = { 8, 65536, ICMP_NOT_ERROR, 0, 0 }, + /* Router advertisement. */ + [9] + = { 8, 8 + 255 * 8, ICMP_NOT_ERROR, 0, 0 }, + /* Router solicitation. */ + [10] + = { 8, 8, ICMP_NOT_ERROR, 0, 0 }, + [ICMP_TIME_EXCEEDED] + = { 8 + 28, 65536, ICMP_IS_ERROR, 0, 1 }, + [ICMP_PARAMETERPROB] + = { 8 + 28, 65536, ICMP_IS_ERROR, 0, 1 }, + [ICMP_TIMESTAMP] + = { 20, 20, ICMP_NOT_ERROR, 0, 0 }, + [ICMP_TIMESTAMPREPLY] + = { 20, 20, ICMP_NOT_ERROR, 0, 0 }, + [ICMP_INFO_REQUEST] + = { 8, 65536, ICMP_NOT_ERROR, 0, 0 }, + [ICMP_INFO_REPLY] + = { 8, 65536, ICMP_NOT_ERROR, 0, 0 }, + [ICMP_ADDRESS] + = { 12, 12, ICMP_NOT_ERROR, 0, 0 }, + [ICMP_ADDRESSREPLY] + = { 12, 12, ICMP_NOT_ERROR, 0, 0 } }; + + /* Can't do anything if it's a fragment. */ + if (offset) + return 1; + + /* Must cover type and code. */ + if (datalen < 2) { + limpk("ICMP len=%u too short\n", datalen); + return 0; + } + + /* If not embedded. */ + if (!embedded) { + /* Bad checksum? Don't print, just ignore. */ + if (!more_frags + && ip_compute_csum((unsigned char *) icmph, datalen) != 0) + return 0; + + /* CHECK: Truncated ICMP (even if first fragment). */ + if (icmph->type < sizeof(info)/sizeof(struct icmp_info) + && info[icmph->type].min_len != 0 + && datalen < info[icmph->type].min_len) { + limpk("ICMP type %u len %u too short\n", + icmph->type, datalen); + return 0; + } + + /* CHECK: Check within known error ICMPs. */ + if (icmph->type < sizeof(info)/sizeof(struct icmp_info) + && info[icmph->type].err == ICMP_IS_ERROR) { + /* CHECK: Embedded packet must be at least + length of iph + 8 bytes. */ + struct iphdr *inner = (void *)icmph + 8; + + /* datalen > 8 since all ICMP_IS_ERROR types + have min length > 8 */ + if (datalen - 8 < sizeof(struct iphdr)) { + limpk("ICMP error internal way too short\n"); + return 0; + } + if (datalen - 8 < inner->ihl*4 + 8) { + limpk("ICMP error internal too short\n"); + return 0; + } + if (!check_ip(inner, datalen - 8, 1)) + return 0; + } + } else { + /* CHECK: Can't embed ICMP unless known non-error. */ + if (icmph->type >= sizeof(info)/sizeof(struct icmp_info) + || info[icmph->type].err != ICMP_NOT_ERROR) { + limpk("ICMP type %u not embeddable\n", + icmph->type); + return 0; + } + } + + /* CHECK: Invalid ICMP codes. */ + if (icmph->type < sizeof(info)/sizeof(struct icmp_info) + && (icmph->code < info[icmph->type].min_code + || icmph->code > info[icmph->type].max_code)) { + limpk("ICMP type=%u code=%u\n", + icmph->type, icmph->code); + return 0; + } + + /* CHECK: Above maximum length. */ + if (icmph->type < sizeof(info)/sizeof(struct icmp_info) + && info[icmph->type].max_len != 0 + && datalen > info[icmph->type].max_len) { + limpk("ICMP type=%u too long: %u bytes\n", + icmph->type, datalen); + return 0; + } + + switch (icmph->type) { + case ICMP_PARAMETERPROB: { + /* CHECK: Problem param must be within error packet's + * IP header. */ + struct iphdr *iph = (void *)icmph + 8; + u_int32_t arg = ntohl(icmph->un.gateway); + + if (icmph->code == 0) { + /* Code 0 means that upper 8 bits is pointer + to problem. */ + if ((arg >> 24) >= iph->ihl*4) { + limpk("ICMP PARAMETERPROB ptr = %u\n", + ntohl(icmph->un.gateway) >> 24); + return 0; + } + arg &= 0x00FFFFFF; + } + + /* CHECK: Rest must be zero. */ + if (arg) { + limpk("ICMP PARAMETERPROB nonzero arg = %u\n", + arg); + return 0; + } + break; + } + + case ICMP_TIME_EXCEEDED: + case ICMP_SOURCE_QUENCH: + /* CHECK: Unused must be zero. */ + if (icmph->un.gateway != 0) { + limpk("ICMP type=%u unused = %u\n", + icmph->type, ntohl(icmph->un.gateway)); + return 0; + } + break; + } + + return 1; +} + +/* UDP-specific checks. */ +static int +check_udp(const struct iphdr *iph, + const struct udphdr *udph, + u_int16_t datalen, + unsigned int offset, + int more_frags, + int embedded) +{ + /* Can't do anything if it's a fragment. */ + if (offset) + return 1; + + /* CHECK: Must cover UDP header. */ + if (datalen < sizeof(struct udphdr)) { + limpk("UDP len=%u too short\n", datalen); + return 0; + } + + /* Bad checksum? Don't print, just say it's unclean. */ + /* FIXME: SRC ROUTE packets won't match checksum --RR */ + if (!more_frags && !embedded && udph->check + && csum_tcpudp_magic(iph->saddr, iph->daddr, datalen, IPPROTO_UDP, + csum_partial((char *)udph, datalen, 0)) != 0) + return 0; + + /* CHECK: Destination port can't be zero. */ + if (!udph->dest) { + limpk("UDP zero destination port\n"); + return 0; + } + + if (!more_frags) { + if (!embedded) { + /* CHECK: UDP length must match. */ + if (ntohs(udph->len) != datalen) { + limpk("UDP len too short %u vs %u\n", + ntohs(udph->len), datalen); + return 0; + } + } else { + /* CHECK: UDP length be >= this truncated pkt. */ + if (ntohs(udph->len) < datalen) { + limpk("UDP len too long %u vs %u\n", + ntohs(udph->len), datalen); + return 0; + } + } + } else { + /* CHECK: UDP length must be > this frag's length. */ + if (ntohs(udph->len) <= datalen) { + limpk("UDP fragment len too short %u vs %u\n", + ntohs(udph->len), datalen); + return 0; + } + } + + return 1; +} + +#define TH_FIN 0x01 +#define TH_SYN 0x02 +#define TH_RST 0x04 +#define TH_PUSH 0x08 +#define TH_ACK 0x10 +#define TH_URG 0x20 +#define TH_ECE 0x40 +#define TH_CWR 0x80 + +/* table of valid flag combinations - ECE and CWR are always valid */ +static u8 tcp_valid_flags[(TH_FIN|TH_SYN|TH_RST|TH_PUSH|TH_ACK|TH_URG) + 1] = +{ + [TH_SYN] = 1, + [TH_SYN|TH_ACK] = 1, + [TH_RST] = 1, + [TH_RST|TH_ACK] = 1, + [TH_RST|TH_ACK|TH_PUSH] = 1, + [TH_FIN|TH_ACK] = 1, + [TH_ACK] = 1, + [TH_ACK|TH_PUSH] = 1, + [TH_ACK|TH_URG] = 1, + [TH_ACK|TH_URG|TH_PUSH] = 1, + [TH_FIN|TH_ACK|TH_PUSH] = 1, + [TH_FIN|TH_ACK|TH_URG] = 1, + [TH_FIN|TH_ACK|TH_URG|TH_PUSH] = 1 +}; + +/* TCP-specific checks. */ +static int +check_tcp(const struct iphdr *iph, + const struct tcphdr *tcph, + u_int16_t datalen, + unsigned int offset, + int more_frags, + int embedded) +{ + u_int8_t *opt = (u_int8_t *)tcph; + u_int8_t *endhdr = (u_int8_t *)tcph + tcph->doff * 4; + u_int8_t tcpflags; + int end_of_options = 0; + size_t i; + + /* CHECK: Can't have offset=1: used to override TCP syn-checks. */ + /* In fact, this is caught below (offset < 516). */ + + /* Can't do anything if it's a fragment. */ + if (offset) + return 1; + + /* CHECK: Smaller than minimal TCP hdr. */ + if (datalen < sizeof(struct tcphdr)) { + if (!embedded) { + limpk("Packet length %u < TCP header.\n", datalen); + return 0; + } + /* Must have ports available (datalen >= 8), from + check_icmp which set embedded = 1 */ + /* CHECK: TCP ports inside ICMP error */ + if (!tcph->source || !tcph->dest) { + limpk("Zero TCP ports %u/%u.\n", + htons(tcph->source), htons(tcph->dest)); + return 0; + } + return 1; + } + + /* CHECK: Smaller than actual TCP hdr. */ + if (datalen < tcph->doff * 4) { + if (!embedded) { + limpk("Packet length %u < actual TCP header.\n", + datalen); + return 0; + } else + return 1; + } + + /* Bad checksum? Don't print, just say it's unclean. */ + /* FIXME: SRC ROUTE packets won't match checksum --RR */ + if (!more_frags && !embedded + && csum_tcpudp_magic(iph->saddr, iph->daddr, datalen, IPPROTO_TCP, + csum_partial((char *)tcph, datalen, 0)) != 0) + return 0; + + /* CHECK: TCP ports non-zero */ + if (!tcph->source || !tcph->dest) { + limpk("Zero TCP ports %u/%u.\n", + htons(tcph->source), htons(tcph->dest)); + return 0; + } + + /* CHECK: TCP reserved bits zero. */ + if(tcp_flag_word(tcph) & TCP_RESERVED_BITS) { + limpk("TCP reserved bits not zero\n"); + return 0; + } + + /* CHECK: TCP flags. */ + tcpflags = (((u_int8_t *)tcph)[13] & ~(TH_ECE|TH_CWR)); + if (!tcp_valid_flags[tcpflags]) { + limpk("TCP flags bad: %u\n", tcpflags); + return 0; + } + + for (i = sizeof(struct tcphdr); i < tcph->doff * 4; ) { + switch (opt[i]) { + case 0: + end_of_options = 1; + i++; + break; + case 1: + i++; + break; + default: + /* CHECK: options after EOO. */ + if (end_of_options) { + limpk("TCP option %u after end\n", + opt[i]); + return 0; + } + /* CHECK: options at tail. */ + else if (i+1 >= tcph->doff * 4) { + limpk("TCP option %u at tail\n", + opt[i]); + return 0; + } + /* CHECK: zero-length options. */ + else if (opt[i+1] == 0) { + limpk("TCP option %u 0 len\n", + opt[i]); + return 0; + } + /* CHECK: oversize options. */ + else if (&opt[i] + opt[i+1] > endhdr) { + limpk("TCP option %u at %Zu too long\n", + (unsigned int) opt[i], i); + return 0; + } + /* Move to next option */ + i += opt[i+1]; + } + } + + return 1; +} + +/* Returns 1 if ok */ +/* Standard IP checks. */ +static int +check_ip(struct iphdr *iph, size_t length, int embedded) +{ + u_int8_t *opt = (u_int8_t *)iph; + u_int8_t *endhdr = (u_int8_t *)iph + iph->ihl * 4; + int end_of_options = 0; + void *protoh; + size_t datalen; + unsigned int i; + unsigned int offset; + + /* Should only happen for local outgoing raw-socket packets. */ + /* CHECK: length >= ip header. */ + if (length < sizeof(struct iphdr) || length < iph->ihl * 4) { + limpk("Packet length %Zu < IP header.\n", length); + return 0; + } + + offset = ntohs(iph->frag_off) & IP_OFFSET; + protoh = (void *)iph + iph->ihl * 4; + datalen = length - iph->ihl * 4; + + /* CHECK: Embedded fragment. */ + if (embedded && offset) { + limpk("Embedded fragment.\n"); + return 0; + } + + for (i = sizeof(struct iphdr); i < iph->ihl * 4; ) { + switch (opt[i]) { + case 0: + end_of_options = 1; + i++; + break; + case 1: + i++; + break; + default: + /* CHECK: options after EOO. */ + if (end_of_options) { + limpk("IP option %u after end\n", + opt[i]); + return 0; + } + /* CHECK: options at tail. */ + else if (i+1 >= iph->ihl * 4) { + limpk("IP option %u at tail\n", + opt[i]); + return 0; + } + /* CHECK: zero-length or one-length options. */ + else if (opt[i+1] < 2) { + limpk("IP option %u %u len\n", + opt[i], opt[i+1]); + return 0; + } + /* CHECK: oversize options. */ + else if (&opt[i] + opt[i+1] > endhdr) { + limpk("IP option %u at %u too long\n", + opt[i], i); + return 0; + } + /* Move to next option */ + i += opt[i+1]; + } + } + + /* Fragment checks. */ + + /* CHECK: More fragments, but doesn't fill 8-byte boundary. */ + if ((ntohs(iph->frag_off) & IP_MF) + && (ntohs(iph->tot_len) % 8) != 0) { + limpk("Truncated fragment %u long.\n", ntohs(iph->tot_len)); + return 0; + } + + /* CHECK: Oversize fragment a-la Ping of Death. */ + if (offset * 8 + datalen > 65535) { + limpk("Oversize fragment to %u.\n", offset * 8); + return 0; + } + + /* CHECK: DF set and offset or MF set. */ + if ((ntohs(iph->frag_off) & IP_DF) + && (offset || (ntohs(iph->frag_off) & IP_MF))) { + limpk("DF set and offset=%u, MF=%u.\n", + offset, ntohs(iph->frag_off) & IP_MF); + return 0; + } + + /* CHECK: Zero-sized fragments. */ + if ((offset || (ntohs(iph->frag_off) & IP_MF)) + && datalen == 0) { + limpk("Zero size fragment offset=%u\n", offset); + return 0; + } + + /* Note: we can have even middle fragments smaller than this: + consider a large packet passing through a 600MTU then + 576MTU link: this gives a fragment of 24 data bytes. But + everyone packs fragments largest first, hence a fragment + can't START before 576 - MAX_IP_HEADER_LEN. */ + + /* Used to be min-size 576: I recall Alan Cox saying ax25 goes + down to 128 (576 taken from RFC 791: All hosts must be + prepared to accept datagrams of up to 576 octets). Use 128 + here. */ +#define MIN_LIKELY_MTU 128 + /* CHECK: Min size of first frag = 128. */ + if ((ntohs(iph->frag_off) & IP_MF) + && offset == 0 + && ntohs(iph->tot_len) < MIN_LIKELY_MTU) { + limpk("First fragment size %u < %u\n", ntohs(iph->tot_len), + MIN_LIKELY_MTU); + return 0; + } + + /* CHECK: Min offset of frag = 128 - IP hdr len. */ + if (offset && offset * 8 < MIN_LIKELY_MTU - iph->ihl * 4) { + limpk("Fragment starts at %u < %u\n", offset * 8, + MIN_LIKELY_MTU - iph->ihl * 4); + return 0; + } + + /* CHECK: Protocol specification non-zero. */ + if (iph->protocol == 0) { + limpk("Zero protocol\n"); + return 0; + } + + /* CHECK: Do not use what is unused. + * First bit of fragmentation flags should be unused. + * May be used by OS fingerprinting tools. + * 04 Jun 2002, Maciej Soltysiak, solt@dns.toxicfilms.tv + */ + if (ntohs(iph->frag_off)>>15) { + limpk("IP unused bit set\n"); + return 0; + } + + /* Per-protocol checks. */ + switch (iph->protocol) { + case IPPROTO_ICMP: + return check_icmp(protoh, datalen, offset, + (ntohs(iph->frag_off) & IP_MF), + embedded); + + case IPPROTO_UDP: + return check_udp(iph, protoh, datalen, offset, + (ntohs(iph->frag_off) & IP_MF), + embedded); + + case IPPROTO_TCP: + return check_tcp(iph, protoh, datalen, offset, + (ntohs(iph->frag_off) & IP_MF), + embedded); + default: + /* Ignorance is bliss. */ + return 1; + } +} + +static int +match(const struct sk_buff *skb, + const struct net_device *in, + const struct net_device *out, + const void *matchinfo, + int offset, + const void *hdr, + u_int16_t datalen, + int *hotdrop) +{ + return !check_ip(skb->nh.iph, skb->len, 0); +} + +/* Called when user tries to insert an entry of this type. */ +static int +checkentry(const char *tablename, + const struct ipt_ip *ip, + void *matchinfo, + unsigned int matchsize, + unsigned int hook_mask) +{ + if (matchsize != IPT_ALIGN(0)) + return 0; + + return 1; +} + +static struct ipt_match unclean_match += { { NULL, NULL }, "unclean", &match, &checkentry, NULL, THIS_MODULE }; + +static int __init init(void) +{ + return ipt_register_match(&unclean_match); +} + +static void __exit fini(void) +{ + ipt_unregister_match(&unclean_match); +} + +module_init(init); +module_exit(fini); +MODULE_LICENSE("GPL"); diff -Nur --exclude '*.orig' linux-2.6.6-rc3.org/net/ipv4/netfilter/iptable_filter.c linux-2.6.6-rc3/net/ipv4/netfilter/iptable_filter.c --- linux-2.6.6-rc3.org/net/ipv4/netfilter/iptable_filter.c 2004-04-28 03:35:43.000000000 +0200 +++ linux-2.6.6-rc3/net/ipv4/netfilter/iptable_filter.c 2004-04-29 11:17:15.000000000 +0200 @@ -59,7 +59,7 @@ 0, sizeof(struct ipt_entry), sizeof(struct ipt_standard), - 0, { 0, 0 }, { } }, + 0, NULL, 0, { 0, 0 }, { } }, { { { { IPT_ALIGN(sizeof(struct ipt_standard_target)), "" } }, { } }, -NF_ACCEPT - 1 } }, /* FORWARD */ @@ -67,7 +67,7 @@ 0, sizeof(struct ipt_entry), sizeof(struct ipt_standard), - 0, { 0, 0 }, { } }, + 0, NULL, 0, { 0, 0 }, { } }, { { { { IPT_ALIGN(sizeof(struct ipt_standard_target)), "" } }, { } }, -NF_ACCEPT - 1 } }, /* LOCAL_OUT */ @@ -75,7 +75,7 @@ 0, sizeof(struct ipt_entry), sizeof(struct ipt_standard), - 0, { 0, 0 }, { } }, + 0, NULL, 0, { 0, 0 }, { } }, { { { { IPT_ALIGN(sizeof(struct ipt_standard_target)), "" } }, { } }, -NF_ACCEPT - 1 } } }, @@ -84,7 +84,7 @@ 0, sizeof(struct ipt_entry), sizeof(struct ipt_error), - 0, { 0, 0 }, { } }, + 0, NULL, 0, { 0, 0 }, { } }, { { { { IPT_ALIGN(sizeof(struct ipt_error_target)), IPT_ERROR_TARGET } }, { } }, "ERROR" diff -Nur --exclude '*.orig' linux-2.6.6-rc3.org/net/ipv4/netfilter/iptable_mangle.c linux-2.6.6-rc3/net/ipv4/netfilter/iptable_mangle.c --- linux-2.6.6-rc3.org/net/ipv4/netfilter/iptable_mangle.c 2004-04-28 03:36:34.000000000 +0200 +++ linux-2.6.6-rc3/net/ipv4/netfilter/iptable_mangle.c 2004-04-29 11:17:15.000000000 +0200 @@ -74,7 +74,7 @@ 0, sizeof(struct ipt_entry), sizeof(struct ipt_standard), - 0, { 0, 0 }, { } }, + 0, NULL, 0, { 0, 0 }, { } }, { { { { IPT_ALIGN(sizeof(struct ipt_standard_target)), "" } }, { } }, -NF_ACCEPT - 1 } }, /* LOCAL_IN */ @@ -82,7 +82,7 @@ 0, sizeof(struct ipt_entry), sizeof(struct ipt_standard), - 0, { 0, 0 }, { } }, + 0, NULL, 0, { 0, 0 }, { } }, { { { { IPT_ALIGN(sizeof(struct ipt_standard_target)), "" } }, { } }, -NF_ACCEPT - 1 } }, /* FORWARD */ @@ -90,7 +90,7 @@ 0, sizeof(struct ipt_entry), sizeof(struct ipt_standard), - 0, { 0, 0 }, { } }, + 0, NULL, 0, { 0, 0 }, { } }, { { { { IPT_ALIGN(sizeof(struct ipt_standard_target)), "" } }, { } }, -NF_ACCEPT - 1 } }, /* LOCAL_OUT */ @@ -98,7 +98,7 @@ 0, sizeof(struct ipt_entry), sizeof(struct ipt_standard), - 0, { 0, 0 }, { } }, + 0, NULL, 0, { 0, 0 }, { } }, { { { { IPT_ALIGN(sizeof(struct ipt_standard_target)), "" } }, { } }, -NF_ACCEPT - 1 } }, /* POST_ROUTING */ @@ -106,7 +106,7 @@ 0, sizeof(struct ipt_entry), sizeof(struct ipt_standard), - 0, { 0, 0 }, { } }, + 0, NULL, 0, { 0, 0 }, { } }, { { { { IPT_ALIGN(sizeof(struct ipt_standard_target)), "" } }, { } }, -NF_ACCEPT - 1 } }, }, @@ -115,7 +115,7 @@ 0, sizeof(struct ipt_entry), sizeof(struct ipt_error), - 0, { 0, 0 }, { } }, + 0, NULL, 0, { 0, 0 }, { } }, { { { { IPT_ALIGN(sizeof(struct ipt_error_target)), IPT_ERROR_TARGET } }, { } }, "ERROR" diff -Nur --exclude '*.orig' linux-2.6.6-rc3.org/net/ipv4/netfilter/iptable_raw.c linux-2.6.6-rc3/net/ipv4/netfilter/iptable_raw.c --- linux-2.6.6-rc3.org/net/ipv4/netfilter/iptable_raw.c 2004-04-28 03:36:36.000000000 +0200 +++ linux-2.6.6-rc3/net/ipv4/netfilter/iptable_raw.c 2004-04-29 11:17:15.000000000 +0200 @@ -46,7 +46,7 @@ 0, sizeof(struct ipt_entry), sizeof(struct ipt_standard), - 0, { 0, 0 }, { } }, + 0, NULL, 0, { 0, 0 }, { } }, { { { { IPT_ALIGN(sizeof(struct ipt_standard_target)), "" } }, { } }, -NF_ACCEPT - 1 } }, /* LOCAL_OUT */ @@ -54,7 +54,7 @@ 0, sizeof(struct ipt_entry), sizeof(struct ipt_standard), - 0, { 0, 0 }, { } }, + 0, NULL, 0, { 0, 0 }, { } }, { { { { IPT_ALIGN(sizeof(struct ipt_standard_target)), "" } }, { } }, -NF_ACCEPT - 1 } } }, @@ -63,7 +63,7 @@ 0, sizeof(struct ipt_entry), sizeof(struct ipt_error), - 0, { 0, 0 }, { } }, + 0, NULL, 0, { 0, 0 }, { } }, { { { { IPT_ALIGN(sizeof(struct ipt_error_target)), IPT_ERROR_TARGET } }, { } }, "ERROR" diff -Nur --exclude '*.orig' linux-2.6.6-rc3.org/net/ipv4/raw.c linux-2.6.6-rc3/net/ipv4/raw.c --- linux-2.6.6-rc3.org/net/ipv4/raw.c 2004-04-28 03:35:49.000000000 +0200 +++ linux-2.6.6-rc3/net/ipv4/raw.c 2004-04-29 11:18:06.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-rc3.org/net/ipv4/tcp_ipv4.c linux-2.6.6-rc3/net/ipv4/tcp_ipv4.c --- linux-2.6.6-rc3.org/net/ipv4/tcp_ipv4.c 2004-04-28 03:35:40.000000000 +0200 +++ linux-2.6.6-rc3/net/ipv4/tcp_ipv4.c 2004-04-29 11:19:32.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-rc3.org/net/ipv4/udp.c linux-2.6.6-rc3/net/ipv4/udp.c --- linux-2.6.6-rc3.org/net/ipv4/udp.c 2004-04-28 03:35:19.000000000 +0200 +++ linux-2.6.6-rc3/net/ipv4/udp.c 2004-04-29 11:19:32.000000000 +0200 @@ -1045,6 +1045,7 @@ kfree_skb(skb); return -1; } + nf_reset(skb); if (up->encap_type) { /* @@ -1210,6 +1211,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)) @@ -1558,6 +1560,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-rc3.org/net/ipv4/xfrm4_tunnel.c linux-2.6.6-rc3/net/ipv4/xfrm4_tunnel.c --- linux-2.6.6-rc3.org/net/ipv4/xfrm4_tunnel.c 2004-04-28 03:35:08.000000000 +0200 +++ linux-2.6.6-rc3/net/ipv4/xfrm4_tunnel.c 2004-04-29 11:18:03.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-rc3.org/net/ipv6/netfilter/Kconfig linux-2.6.6-rc3/net/ipv6/netfilter/Kconfig --- linux-2.6.6-rc3.org/net/ipv6/netfilter/Kconfig 2004-04-29 11:24:39.000000000 +0200 +++ linux-2.6.6-rc3/net/ipv6/netfilter/Kconfig 2004-04-29 11:19:41.000000000 +0200 @@ -255,5 +255,29 @@ depends on IP6_NF_IPTABLES help +config IP6_NF_TARGET_TRACE + tristate 'TRACE target support' + depends on IP6_NF_RAW + help + The TRACE target allows packets to be traced as those + matches any subsequent rule in any table/rule. The matched + rule and the packet is logged with the prefix + + TRACE: tablename/chainname/rulenum + + If you want to compile it as a module, say M here and read + . If unsure, say `N'. + help + +config IP6_NF_MATCH_POLICY + tristate "IPsec policy match support" + depends on IP6_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. + endmenu diff -Nur --exclude '*.orig' linux-2.6.6-rc3.org/net/ipv6/netfilter/Makefile linux-2.6.6-rc3/net/ipv6/netfilter/Makefile --- linux-2.6.6-rc3.org/net/ipv6/netfilter/Makefile 2004-04-29 11:24:39.000000000 +0200 +++ linux-2.6.6-rc3/net/ipv6/netfilter/Makefile 2004-04-29 11:19:41.000000000 +0200 @@ -29,4 +29,6 @@ obj-$(CONFIG_IP6_NF_MATCH_NTH) += ip6t_nth.o obj-$(CONFIG_IP6_NF_TARGET_HL) += ip6t_HL.o obj-$(CONFIG_IP6_NF_RAW) += ip6table_raw.o +obj-$(CONFIG_IP6_NF_TARGET_TRACE) += ip6t_TRACE.o obj-$(CONFIG_IP6_NF_MATCH_HL) += ip6t_hl.o +obj-$(CONFIG_IP6_NF_MATCH_POLICY) += ip6t_policy.o diff -Nur --exclude '*.orig' linux-2.6.6-rc3.org/net/ipv6/netfilter/ip6_tables.c linux-2.6.6-rc3/net/ipv6/netfilter/ip6_tables.c --- linux-2.6.6-rc3.org/net/ipv6/netfilter/ip6_tables.c 2004-04-28 03:36:34.000000000 +0200 +++ linux-2.6.6-rc3/net/ipv6/netfilter/ip6_tables.c 2004-04-29 11:17:15.000000000 +0200 @@ -38,6 +38,14 @@ #define IPV6_HDR_LEN (sizeof(struct ipv6hdr)) #define IPV6_OPTHDR_LEN (sizeof(struct ipv6_opt_hdr)) +static const char *hook6names[] = { + [NF_IP6_PRE_ROUTING] "PREROUTING", + [NF_IP6_LOCAL_IN] "INPUT", + [NF_IP6_FORWARD] "FORWARD", + [NF_IP6_LOCAL_OUT] "OUTPUT", + [NF_IP6_POST_ROUTING] "POSTROUTING", +}; + /*#define DEBUG_IP_FIREWALL*/ /*#define DEBUG_ALLOW_ALL*/ /* Useful for remote debugging */ /*#define DEBUG_IP_FIREWALL_USER*/ @@ -408,6 +416,12 @@ t = ip6t_get_target(e); IP_NF_ASSERT(t->u.kernel.target); + + /* The packet traced and the rule isn't an unconditional return/END. */ + if (((*pskb)->nfcache & NFC_TRACE) && e->rulenum) { + nf_log_packet(PF_INET6, hook, *pskb, in, out, "TRACE: %s/%s/%u ", + table->name, e->chainname, e->rulenum); + } /* Standard target? */ if (!t->u.kernel.target->target) { int v; @@ -561,6 +575,29 @@ return find_inlist_lock(&ip6t_target, name, "ip6t_", error, mutex); } +static inline int +find_error_target(struct ip6t_entry *s, + struct ip6t_entry *e, + char **chainname) +{ + struct ip6t_entry_target *t; + static struct ip6t_entry *found = NULL; + + if (s == e) { + if (!found) + return 0; + t = ip6t_get_target(found); + if (strcmp(t->u.user.name, + IP6T_ERROR_TARGET) == 0) { + *chainname = t->data; + return 1; + } + } else + found = s; + + return 0; +} + /* All zeroes == unconditional rule. */ static inline int unconditional(const struct ip6t_ip6 *ipv6) @@ -580,6 +617,8 @@ mark_source_chains(struct ip6t_table_info *newinfo, unsigned int valid_hooks) { unsigned int hook; + char *chainname = NULL; + u_int32_t rulenum; /* No recursion; use packet counter to save back ptrs (reset to 0 as we leave), and comefrom to save source hook bitmask */ @@ -593,6 +632,8 @@ /* Set initial back pointer. */ e->counters.pcnt = pos; + rulenum = 1; + chainname = (char *) hook6names[hook]; for (;;) { struct ip6t_standard_target *t @@ -605,6 +646,8 @@ } e->comefrom |= ((1 << hook) | (1 << NF_IP6_NUMHOOKS)); + e->rulenum = rulenum++; + e->chainname = chainname; /* Unconditional return/END. */ if (e->target_offset == sizeof(struct ip6t_entry) @@ -614,6 +657,10 @@ && unconditional(&e->ipv6)) { unsigned int oldpos, size; + /* Set unconditional rulenum to zero. */ + e->rulenum = 0; + e->counters.bcnt = 0; + /* Return: backtrack through the last big jump. */ do { @@ -639,6 +686,11 @@ (newinfo->entries + pos); } while (oldpos == pos + e->next_offset); + /* Restore chainname, rulenum. */ + chainname = e->chainname; + rulenum = e->counters.bcnt; + e->counters.bcnt = 0; + /* Move along one */ size = e->next_offset; e = (struct ip6t_entry *) @@ -654,6 +706,17 @@ /* This a jump; chase it. */ duprintf("Jump rule %u -> %u\n", pos, newpos); + e->counters.bcnt = rulenum++; + rulenum = 1; + e = (struct ip6t_entry *) + (newinfo->entries + newpos); + if (IP6T_ENTRY_ITERATE(newinfo->entries, + newinfo->size, + find_error_target, + e, &chainname) == 0) { + printk("ip6_tables: table screwed up!\n"); + return 0; + } } else { /* ... this is a fallthru */ newpos = pos + e->next_offset; diff -Nur --exclude '*.orig' linux-2.6.6-rc3.org/net/ipv6/netfilter/ip6t_TRACE.c linux-2.6.6-rc3/net/ipv6/netfilter/ip6t_TRACE.c --- linux-2.6.6-rc3.org/net/ipv6/netfilter/ip6t_TRACE.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.6-rc3/net/ipv6/netfilter/ip6t_TRACE.c 2004-04-29 11:17:15.000000000 +0200 @@ -0,0 +1,65 @@ +/* This is a module which is used for setting + * the NFC_TRACE flag in the nfcache field of an skb. + */ +#include +#include + +#include + +MODULE_LICENSE("GPL"); + +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) +{ + (*pskb)->nfcache |= NFC_TRACE; + return IP6T_CONTINUE; +} + +static int +checkentry(const char *tablename, + const struct ip6t_entry *e, + void *targinfo, + unsigned int targinfosize, + unsigned int hook_mask) +{ + if (targinfosize != 0) { + printk(KERN_WARNING "TRACE: targinfosize %u != 0\n", + targinfosize); + return 0; + } + + if (strcmp(tablename, "raw") != 0) { + printk(KERN_WARNING "TRACE: can only be called from \"raw\" table, not \"%s\"\n", tablename); + return 0; + } + + return 1; +} + +static struct ip6t_target ip6t_trace_reg = { + .name = "TRACE", + .target = target, + .checkentry = checkentry, + .me = THIS_MODULE +}; + +static int __init init(void) +{ + if (ip6t_register_target(&ip6t_trace_reg)) + return -EINVAL; + + return 0; +} + +static void __exit fini(void) +{ + ip6t_unregister_target(&ip6t_trace_reg); +} + +module_init(init); +module_exit(fini); diff -Nur --exclude '*.orig' linux-2.6.6-rc3.org/net/ipv6/netfilter/ip6t_owner.c linux-2.6.6-rc3/net/ipv6/netfilter/ip6t_owner.c --- linux-2.6.6-rc3.org/net/ipv6/netfilter/ip6t_owner.c 2004-04-28 03:36:30.000000000 +0200 +++ linux-2.6.6-rc3/net/ipv6/netfilter/ip6t_owner.c 2004-04-29 11:19:40.000000000 +0200 @@ -21,6 +21,39 @@ MODULE_LICENSE("GPL"); static int +match_comm(const struct sk_buff *skb, const char *comm) +{ + struct task_struct *p, *g; + struct files_struct *files; + int i; + + read_lock(&tasklist_lock); + do_each_thread(g, 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->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); + } while_each_thread(g, 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 +158,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-rc3.org/net/ipv6/netfilter/ip6t_policy.c linux-2.6.6-rc3/net/ipv6/netfilter/ip6t_policy.c --- linux-2.6.6-rc3.org/net/ipv6/netfilter/ip6t_policy.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.6-rc3/net/ipv6/netfilter/ip6t_policy.c 2004-04-29 11:19:41.000000000 +0200 @@ -0,0 +1,200 @@ +/* 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 ip6_masked_addrcmp(struct in6_addr addr1, struct in6_addr mask, + struct in6_addr addr2) +{ + int i; + + for (i = 0; i < 16; i++) { + if ((addr1.s6_addr[i] & mask.s6_addr[i]) != + (addr2.s6_addr[i] & mask.s6_addr[i])) + return 1; + } + return 0; +} + + +static inline int +match_xfrm_state(struct xfrm_state *x, const struct ip6t_policy_elem *e) +{ +#define MISMATCH(x,y) (e->match.x && ((e->x != (y)) ^ e->invert.x)) + + struct in6_addr xfrm_saddr, xfrm_daddr; + + if ((e->match.saddr + && (ip6_masked_addrcmp(xfrm_saddr, e->saddr, e->smask)) + ^ e->invert.saddr ) || + (e->match.daddr + && (ip6_masked_addrcmp(xfrm_daddr, e->daddr, e->dmask)) + ^ e->invert.daddr ) || + 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 ip6t_policy_info *info) +{ + const struct ip6t_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 ip6t_policy_info *info) +{ + const struct ip6t_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, + const void *hdr, + u_int16_t datalen, + int *hotdrop) +{ + const struct ip6t_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 ip6t_ip6 *ip, + void *matchinfo, unsigned int matchsize, + unsigned int hook_mask) +{ + struct ip6t_policy_info *info = matchinfo; + + if (matchsize != IP6T_ALIGN(sizeof(*info))) { + printk(KERN_ERR "ip6t_policy: matchsize %u != %u\n", + matchsize, IP6T_ALIGN(sizeof(*info))); + return 0; + } + if (!(info->flags & (POLICY_MATCH_IN|POLICY_MATCH_OUT))) { + printk(KERN_ERR "ip6t_policy: neither incoming nor " + "outgoing policy selected\n"); + return 0; + } + if (hook_mask & (1 << NF_IP6_PRE_ROUTING | 1 << NF_IP6_LOCAL_IN) + && info->flags & POLICY_MATCH_OUT) { + printk(KERN_ERR "ip6t_policy: output policy not valid in " + "PRE_ROUTING and INPUT\n"); + return 0; + } + if (hook_mask & (1 << NF_IP6_POST_ROUTING | 1 << NF_IP6_LOCAL_OUT) + && info->flags & POLICY_MATCH_IN) { + printk(KERN_ERR "ip6t_policy: input policy not valid in " + "POST_ROUTING and OUTPUT\n"); + return 0; + } + if (info->len > POLICY_MAX_ELEM) { + printk(KERN_ERR "ip6t_policy: too many policy elements\n"); + return 0; + } + + return 1; +} + +static struct ip6t_match policy_match = +{ + .name = "policy", + .match = match, + .checkentry = checkentry, + .me = THIS_MODULE, +}; + +static int __init init(void) +{ + return ip6t_register_match(&policy_match); +} + +static void __exit fini(void) +{ + ip6t_unregister_match(&policy_match); +} + +module_init(init); +module_exit(fini); diff -Nur --exclude '*.orig' linux-2.6.6-rc3.org/net/ipv6/netfilter/ip6table_filter.c linux-2.6.6-rc3/net/ipv6/netfilter/ip6table_filter.c --- linux-2.6.6-rc3.org/net/ipv6/netfilter/ip6table_filter.c 2004-04-28 03:35:07.000000000 +0200 +++ linux-2.6.6-rc3/net/ipv6/netfilter/ip6table_filter.c 2004-04-29 11:17:15.000000000 +0200 @@ -58,7 +58,7 @@ 0, sizeof(struct ip6t_entry), sizeof(struct ip6t_standard), - 0, { 0, 0 }, { } }, + 0, NULL, 0, { 0, 0 }, { } }, { { { { IP6T_ALIGN(sizeof(struct ip6t_standard_target)), "" } }, { } }, -NF_ACCEPT - 1 } }, /* FORWARD */ @@ -66,7 +66,7 @@ 0, sizeof(struct ip6t_entry), sizeof(struct ip6t_standard), - 0, { 0, 0 }, { } }, + 0, NULL, 0, { 0, 0 }, { } }, { { { { IP6T_ALIGN(sizeof(struct ip6t_standard_target)), "" } }, { } }, -NF_ACCEPT - 1 } }, /* LOCAL_OUT */ @@ -74,7 +74,7 @@ 0, sizeof(struct ip6t_entry), sizeof(struct ip6t_standard), - 0, { 0, 0 }, { } }, + 0, NULL, 0, { 0, 0 }, { } }, { { { { IP6T_ALIGN(sizeof(struct ip6t_standard_target)), "" } }, { } }, -NF_ACCEPT - 1 } } }, @@ -83,7 +83,7 @@ 0, sizeof(struct ip6t_entry), sizeof(struct ip6t_error), - 0, { 0, 0 }, { } }, + 0, NULL, 0, { 0, 0 }, { } }, { { { { IP6T_ALIGN(sizeof(struct ip6t_error_target)), IP6T_ERROR_TARGET } }, { } }, "ERROR" diff -Nur --exclude '*.orig' linux-2.6.6-rc3.org/net/ipv6/netfilter/ip6table_mangle.c linux-2.6.6-rc3/net/ipv6/netfilter/ip6table_mangle.c --- linux-2.6.6-rc3.org/net/ipv6/netfilter/ip6table_mangle.c 2004-04-28 03:35:43.000000000 +0200 +++ linux-2.6.6-rc3/net/ipv6/netfilter/ip6table_mangle.c 2004-04-29 11:17:15.000000000 +0200 @@ -73,7 +73,7 @@ 0, sizeof(struct ip6t_entry), sizeof(struct ip6t_standard), - 0, { 0, 0 }, { } }, + 0, NULL, 0, { 0, 0 }, { } }, { { { { IP6T_ALIGN(sizeof(struct ip6t_standard_target)), "" } }, { } }, -NF_ACCEPT - 1 } }, /* LOCAL_IN */ @@ -81,7 +81,7 @@ 0, sizeof(struct ip6t_entry), sizeof(struct ip6t_standard), - 0, { 0, 0 }, { } }, + 0, NULL, 0, { 0, 0 }, { } }, { { { { IP6T_ALIGN(sizeof(struct ip6t_standard_target)), "" } }, { } }, -NF_ACCEPT - 1 } }, /* FORWARD */ @@ -89,7 +89,7 @@ 0, sizeof(struct ip6t_entry), sizeof(struct ip6t_standard), - 0, { 0, 0 }, { } }, + 0, NULL, 0, { 0, 0 }, { } }, { { { { IP6T_ALIGN(sizeof(struct ip6t_standard_target)), "" } }, { } }, -NF_ACCEPT - 1 } }, /* LOCAL_OUT */ @@ -97,7 +97,7 @@ 0, sizeof(struct ip6t_entry), sizeof(struct ip6t_standard), - 0, { 0, 0 }, { } }, + 0, NULL, 0, { 0, 0 }, { } }, { { { { IP6T_ALIGN(sizeof(struct ip6t_standard_target)), "" } }, { } }, -NF_ACCEPT - 1 } }, /* POST_ROUTING */ @@ -105,7 +105,7 @@ 0, sizeof(struct ip6t_entry), sizeof(struct ip6t_standard), - 0, { 0, 0 }, { } }, + 0, NULL, 0, { 0, 0 }, { } }, { { { { IP6T_ALIGN(sizeof(struct ip6t_standard_target)), "" } }, { } }, -NF_ACCEPT - 1 } } }, @@ -114,7 +114,7 @@ 0, sizeof(struct ip6t_entry), sizeof(struct ip6t_error), - 0, { 0, 0 }, { } }, + 0, NULL, 0, { 0, 0 }, { } }, { { { { IP6T_ALIGN(sizeof(struct ip6t_error_target)), IP6T_ERROR_TARGET } }, { } }, "ERROR" diff -Nur --exclude '*.orig' linux-2.6.6-rc3.org/net/ipv6/netfilter/ip6table_raw.c linux-2.6.6-rc3/net/ipv6/netfilter/ip6table_raw.c --- linux-2.6.6-rc3.org/net/ipv6/netfilter/ip6table_raw.c 2004-04-28 03:36:19.000000000 +0200 +++ linux-2.6.6-rc3/net/ipv6/netfilter/ip6table_raw.c 2004-04-29 11:17:15.000000000 +0200 @@ -52,7 +52,7 @@ 0, sizeof(struct ip6t_entry), sizeof(struct ip6t_standard), - 0, { 0, 0 }, { } }, + 0, NULL, 0, { 0, 0 }, { } }, { { { { IP6T_ALIGN(sizeof(struct ip6t_standard_target)), "" } }, { } }, -NF_ACCEPT - 1 } }, /* LOCAL_OUT */ @@ -60,7 +60,7 @@ 0, sizeof(struct ip6t_entry), sizeof(struct ip6t_standard), - 0, { 0, 0 }, { } }, + 0, NULL, 0, { 0, 0 }, { } }, { { { { IP6T_ALIGN(sizeof(struct ip6t_standard_target)), "" } }, { } }, -NF_ACCEPT - 1 } }, }, @@ -69,7 +69,7 @@ 0, sizeof(struct ip6t_entry), sizeof(struct ip6t_error), - 0, { 0, 0 }, { } }, + 0, NULL, 0, { 0, 0 }, { } }, { { { { IP6T_ALIGN(sizeof(struct ip6t_error_target)), IP6T_ERROR_TARGET } }, { } }, "ERROR" diff -Nur --exclude '*.orig' linux-2.6.6-rc3.org/net/xfrm/xfrm_input.c linux-2.6.6-rc3/net/xfrm/xfrm_input.c --- linux-2.6.6-rc3.org/net/xfrm/xfrm_input.c 2004-04-28 03:35:05.000000000 +0200 +++ linux-2.6.6-rc3/net/xfrm/xfrm_input.c 2004-04-29 11:18:03.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-rc3.org/net/xfrm/xfrm_policy.c linux-2.6.6-rc3/net/xfrm/xfrm_policy.c --- linux-2.6.6-rc3.org/net/xfrm/xfrm_policy.c 2004-04-28 03:35:48.000000000 +0200 +++ linux-2.6.6-rc3/net/xfrm/xfrm_policy.c 2004-04-29 11:18:06.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) {