diff options
author | nico <nico@3c298f89-4303-0410-b956-a3cf2f4a3e73> | 2005-11-13 23:18:09 +0000 |
---|---|---|
committer | nico <nico@3c298f89-4303-0410-b956-a3cf2f4a3e73> | 2005-11-13 23:18:09 +0000 |
commit | 5cf4c5a03cefeab29ce2426aab574cde6bcad5d2 (patch) | |
tree | 0d5e412051004c5527e8eba1fe8530bf278fcb44 /openwrt/target/linux/linux-2.4/patches/generic | |
parent | 28305cf4cf4731566d0e966620ab00baf1058fdb (diff) |
rename netfilter patches to be in the 6xx range
git-svn-id: svn://svn.openwrt.org/openwrt/trunk@2476 3c298f89-4303-0410-b956-a3cf2f4a3e73
Diffstat (limited to 'openwrt/target/linux/linux-2.4/patches/generic')
8 files changed, 11617 insertions, 0 deletions
diff --git a/openwrt/target/linux/linux-2.4/patches/generic/601-netfilter_ipp2p.patch b/openwrt/target/linux/linux-2.4/patches/generic/601-netfilter_ipp2p.patch new file mode 100644 index 000000000..1314ae4fe --- /dev/null +++ b/openwrt/target/linux/linux-2.4/patches/generic/601-netfilter_ipp2p.patch @@ -0,0 +1,720 @@ +diff -urN linux-2.4.29.old/include/linux/netfilter_ipv4/ipt_ipp2p.h linux-2.4.29/include/linux/netfilter_ipv4/ipt_ipp2p.h +--- linux-2.4.29.old/include/linux/netfilter_ipv4/ipt_ipp2p.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.4.29/include/linux/netfilter_ipv4/ipt_ipp2p.h 2005-03-12 00:44:17.000000000 +0100 +@@ -0,0 +1,29 @@ ++#ifndef __IPT_IPP2P_H ++#define __IPT_IPP2P_H ++#define IPP2P_VERSION "0.7.4" ++ ++struct ipt_p2p_info { ++ int cmd; ++ int debug; ++}; ++ ++#endif //__IPT_IPP2P_H ++ ++#define SHORT_HAND_IPP2P 1 /* --ipp2p switch*/ ++#define SHORT_HAND_DATA 4 /* --ipp2p-data switch*/ ++#define SHORT_HAND_NONE 5 /* no short hand*/ ++ ++#define IPP2P_EDK 2 ++#define IPP2P_DATA_KAZAA 8 ++#define IPP2P_DATA_EDK 16 ++#define IPP2P_DATA_DC 32 ++#define IPP2P_DC 64 ++#define IPP2P_DATA_GNU 128 ++#define IPP2P_GNU 256 ++#define IPP2P_KAZAA 512 ++#define IPP2P_BIT 1024 ++#define IPP2P_APPLE 2048 ++#define IPP2P_SOUL 4096 ++#define IPP2P_WINMX 8192 ++#define IPP2P_ARES 16384 ++ +diff -urN linux-2.4.29.old/net/ipv4/netfilter/Config.in linux-2.4.29/net/ipv4/netfilter/Config.in +--- linux-2.4.29.old/net/ipv4/netfilter/Config.in 2005-03-12 00:40:38.000000000 +0100 ++++ linux-2.4.29/net/ipv4/netfilter/Config.in 2005-03-12 00:42:57.000000000 +0100 +@@ -26,6 +26,7 @@ + dep_tristate ' TOS match support' CONFIG_IP_NF_MATCH_TOS $CONFIG_IP_NF_IPTABLES + dep_tristate ' recent match support' CONFIG_IP_NF_MATCH_RECENT $CONFIG_IP_NF_IPTABLES + dep_tristate ' ECN match support' CONFIG_IP_NF_MATCH_ECN $CONFIG_IP_NF_IPTABLES ++ dep_tristate ' peer to peer traffic match support' CONFIG_IP_NF_MATCH_IPP2P $CONFIG_IP_NF_IPTABLES + + dep_tristate ' DSCP match support' CONFIG_IP_NF_MATCH_DSCP $CONFIG_IP_NF_IPTABLES + +diff -urN linux-2.4.29.old/net/ipv4/netfilter/ipt_ipp2p.c linux-2.4.29/net/ipv4/netfilter/ipt_ipp2p.c +--- linux-2.4.29.old/net/ipv4/netfilter/ipt_ipp2p.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.4.29/net/ipv4/netfilter/ipt_ipp2p.c 2005-03-12 00:44:02.000000000 +0100 +@@ -0,0 +1,661 @@ ++#if defined(MODVERSIONS) ++#include <linux/modversions.h> ++#endif ++#include <linux/module.h> ++#include <linux/netfilter_ipv4/ip_tables.h> ++#include <linux/version.h> ++#include <linux/netfilter_ipv4/ipt_ipp2p.h> ++//#include "ipt_ipp2p.h" ++#include <net/tcp.h> ++#include <net/udp.h> ++ ++#define get_u8(X,O) (*(__u8 *)(X + O)) ++#define get_u16(X,O) (*(__u16 *)(X + O)) ++#define get_u32(X,O) (*(__u32 *)(X + O)) ++ ++MODULE_AUTHOR("Eicke Friedrich <ipp2p@ipp2p.org>"); ++MODULE_DESCRIPTION("An extension to iptables to identify P2P traffic."); ++MODULE_LICENSE("GPL"); ++ ++ ++/*Search for UDP eDonkey/eMule/Kad commands*/ ++int ++udp_search_edk (unsigned char *haystack, int packet_len) ++{ ++ unsigned char *t = haystack; ++ t += 8; ++ ++ switch (t[0]) { ++ case 0xe3: { /*edonkey*/ ++ switch (t[1]) { ++ /* e3 9a + 16Bytes Hash | size == 26 */ ++ case 0x9a: if (packet_len == 26) return ((IPP2P_EDK * 100) + 1); ++ /* e3 96 xx yy zz kk | size == 14 | server status request */ ++ case 0x96: if (packet_len == 14) return ((IPP2P_EDK * 100) + 2); ++ /* e3 a2 | size == 10 or 14 <-- recheck*/ ++ } ++ } ++ ++ case 0xc5: { /*emule*/ ++ switch (t[1]) { ++ /* c5 91 xx yy | size == 12 (8+4) | xx != 0x00 -- xx yy queue rating */ ++ case 0x91: if ((packet_len == 12) && (t[2] != 0x00)) return ((IPP2P_EDK * 100) + 3); ++ /* c5 90 xx .. yy | size == 26 (8+2+16) | xx .. yy == hash -- file ping */ ++ case 0x90: if ((packet_len == 26) && (t[2] != 0x00)) return ((IPP2P_EDK * 100) + 4); ++ /* c5 92 | size == 10 (8+2) -- file not found */ ++ case 0x92: if (packet_len == 10) return ((IPP2P_EDK * 100) + 5); ++ /* c5 93 | size == 10 (8+2) -- queue full */ ++ case 0x93: if (packet_len == 10) return ((IPP2P_EDK * 100) + 6); ++ } ++ } ++ ++ case 0xe4: { /*kad*/ ++ switch (t[1]) { ++ /* e4 50 | size == 12 */ ++ case 0x50: if (packet_len == 12) return ((IPP2P_EDK * 100) + 7); ++ /* e4 58 | size == 14 */ ++ case 0x58: if ((packet_len == 14) && (t[2] != 0x00)) return ((IPP2P_EDK * 100) + 8); ++ /* e4 59 | size == 10 */ ++ case 0x59: if (packet_len == 10) return ((IPP2P_EDK * 100) + 9); ++ /* e4 30 .. | t[18] == 0x01 | size > 26 | --> search */ ++ case 0x30: if ((packet_len > 26) && (t[18] == 0x01)) return ((IPP2P_EDK * 100) + 10); ++ /* e4 28 .. 00 | t[68] == 0x00 | size > 76 */ ++ case 0x28: if ((packet_len > 76) && (t[68] == 0x00)) return ((IPP2P_EDK * 100) + 11); ++ /* e4 20 .. | size == 43 */ ++ case 0x20: if ((packet_len == 43) && (t[2] != 0x00) && (t[34] != 0x00)) return ((IPP2P_EDK * 100) + 12); ++ /* e4 00 .. 00 | size == 35 ? */ ++ case 0x00: if ((packet_len == 35) && (t[26] == 0x00)) return ((IPP2P_EDK * 100) + 13); ++ /* e4 10 .. 00 | size == 35 ? */ ++ case 0x10: if ((packet_len == 35) && (t[26] == 0x00)) return ((IPP2P_EDK * 100) + 14); ++ /* e4 18 .. 00 | size == 35 ? */ ++ case 0x18: if ((packet_len == 35) && (t[26] == 0x00)) return ((IPP2P_EDK * 100) + 15); ++ /* e4 40 .. | t[18] == 0x01 | t[19] == 0x00 | size > 40 */ ++ case 0x40: if ((packet_len > 40) && (t[18] == 0x01) && (t[19] == 0x00)) return ((IPP2P_EDK * 100) + 16); ++ } ++ } ++ ++ default: return 0; ++ } /* end of switch (t[0]) */ ++}/*udp_search_edk*/ ++ ++ ++/*Search for UDP Gnutella commands*/ ++int ++udp_search_gnu (unsigned char *haystack, int packet_len) ++{ ++ unsigned char *t = haystack; ++ t += 8; ++ ++ if (memcmp(t, "GND", 3) == 0) return ((IPP2P_GNU * 100) + 1); ++ if (memcmp(t, "GNUTELLA ", 9) == 0) return ((IPP2P_GNU * 100) + 2); ++ return 0; ++}/*udp_search_gnu*/ ++ ++ ++/*Search for UDP KaZaA commands*/ ++int ++udp_search_kazaa (unsigned char *haystack, int packet_len) ++{ ++ unsigned char *t = haystack; ++ ++ if (t[packet_len-1] == 0x00){ ++ t += (packet_len - 6); ++ if (memcmp(t, "KaZaA", 5) == 0) return (IPP2P_KAZAA * 100); ++ } ++ return 0; ++}/*udp_search_kazaa*/ ++ ++ ++/*Search for UDP BitTorrent commands*/ ++int ++udp_search_bit (unsigned char *haystack, int packet_len) ++{ ++ unsigned char *t = haystack; ++ ++ /* packet_len has to be 24 */ ++ if (packet_len != 24) return 0; ++ ++ t += 8; ++ ++ /* ^ 00 00 04 17 27 10 19 80 */ ++ if ((ntohl(get_u32(t, 0)) == 0x00000417) && (ntohl(get_u32(t, 4)) == 0x27101980)) return (IPP2P_BIT * 100); ++ ++ return 0; ++}/*udp_search_bit*/ ++ ++ ++ ++/*Search for Ares commands*/ ++int ++search_ares (unsigned char *haystack, int packet_len, int head_len) ++{ ++ unsigned char *t = haystack; ++ t += head_len; ++ ++ if ((packet_len - head_len) == 6){ /* possible connect command*/ ++ if ((t[0] == 0x03) && (t[1] == 0x00) && (t[2] == 0x5a) && (t[3] == 0x04) && (t[4] == 0x03) && (t[5] == 0x05)) ++ return ((IPP2P_ARES * 100) + 1); /* found connect packet: 03 00 5a 04 03 05 */ ++ } ++ if ((packet_len - head_len) == 60){ /* possible download command*/ ++ if ((t[59] == 0x0a) && (t[58] == 0x0a)){ ++ if (memcmp(t, "PUSH SHA1:", 10) == 0) /* found download command */ ++ return ((IPP2P_ARES * 100) + 2); ++ } ++ } ++ return 0; ++} /*search_ares*/ ++ ++ ++/*Search for SoulSeek commands*/ ++int ++search_soul (unsigned char *haystack, int packet_len, int head_len) ++{ ++ unsigned char *t = haystack; ++ t += head_len; ++ ++ if (get_u16(t, 0) == (packet_len - head_len - 4)){ ++ /* xx xx 00 00 yy zz 00 00 .. | xx = sizeof(payload) - 4 */ ++ if ((get_u16(t,2) == 0x0000) &&(t[4] != 0x00) && (get_u16(t,6) == 0x0000)) ++ return ((IPP2P_SOUL * 100) + 1); ++ } else { ++ /* 00 00 00 00 00 00 00 00 + sizeof(payload) == 8*/ ++ if (((packet_len - head_len) == 8) && (get_u32(t, 0) == 0x00000000) && (get_u32(t, 4) == 0x00000000)) ++ return ((IPP2P_SOUL * 100) + 2); ++ } ++ ++ /* 01 xx 00 00 00 yy .. zz 00 00 00 .. | xx == sizeof(nick) | yy .. zz == nick */ ++ if ((t[0] == 0x01) && (t[2] == 0x00) && (get_u16(t,3) == 0x0000) && ((packet_len - head_len) > ((get_u8(t,1))+6)) && ++ (t[(get_u8(t,1))+4] != 0x00) && (t[(get_u8(t,1))+5] == 0x01) && (t[(get_u8(t,1))+6] == 0x00)) ++ return ((IPP2P_SOUL * 100) + 3); ++ return 0; ++} ++ ++ ++/*Search for WinMX commands*/ ++int ++search_winmx (unsigned char *haystack, int packet_len, int head_len) ++{ ++ unsigned char *t = haystack; ++ int c; ++ t += head_len; ++ ++ if (((packet_len - head_len) == 4) && (memcmp(t, "SEND", 4) == 0)) return ((IPP2P_WINMX * 100) + 1); ++ if (((packet_len - head_len) == 3) && (memcmp(t, "GET", 3) == 0)) return ((IPP2P_WINMX * 100) + 2); ++ if (packet_len < (head_len + 10)) return 0; ++ ++ if ((memcmp(t, "SEND", 4) == 0) || (memcmp(t, "GET", 3) == 0)){ ++ c = head_len + 4; ++ t += 4; ++ while (c < packet_len - 5) { ++ if ((t[0] == 0x20) && (t[1] == 0x22)){ ++ c += 2; ++ t += 2; ++ while (c < packet_len - 2) { ++ if ((t[0] == 0x22) && (t[1] == 0x20)) return ((IPP2P_WINMX * 100) + 3); ++ t++; ++ c++; ++ } ++ } ++ t++; ++ c++; ++ } ++ } ++ return 0; ++} /*search_winmx*/ ++ ++ ++/*Search for appleJuice commands*/ ++int ++search_apple (unsigned char *haystack, int packet_len, int head_len) ++{ ++ unsigned char *t = haystack; ++ t += head_len; ++ ++ if ((memcmp(t, "ajprot", 6) == 0) && (t[6] == 0x0d) && (t[7] == 0x0a)) return (IPP2P_APPLE * 100); ++ ++ return 0; ++} ++ ++ ++/*Search for BitTorrent commands*/ ++int ++search_bittorrent (unsigned char *haystack, int packet_len, int head_len) ++{ ++ ++ unsigned char *t = haystack; ++ if (*(haystack+head_len) != 0x13) return 0; /*Bail out of first byte != 0x13*/ ++ ++ t += head_len + 1; ++ ++ if (memcmp(t, "BitTorrent protocol", 19) == 0) return (IPP2P_BIT * 100); ++ return 0; ++} ++ ++ ++ ++/*check for Kazaa get command*/ ++int ++search_kazaa (unsigned char *haystack, int packet_len, int head_len) ++{ ++ unsigned char *t = haystack; ++ ++ if (!((*(haystack + packet_len - 2) == 0x0d) && (*(haystack + packet_len - 1) == 0x0a))) return 0; ++ ++ t += head_len; ++ if (memcmp(t, "GET /.hash=", 11) == 0) ++ return (IPP2P_DATA_KAZAA * 100); ++ else ++ return 0; ++} ++ ++ ++/*check for gnutella get command*/ ++int ++search_gnu (unsigned char *haystack, int packet_len, int head_len) ++{ ++ unsigned char *t = haystack; ++ ++ if (!((*(haystack + packet_len - 2) == 0x0d) && (*(haystack + packet_len - 1) == 0x0a))) return 0; ++ ++ t += head_len; ++ if (memcmp(t, "GET /get/", 9) == 0) return ((IPP2P_DATA_GNU * 100) + 1); ++ if (memcmp(t, "GET /uri-res/", 13) == 0) return ((IPP2P_DATA_GNU * 100) + 2); ++ ++ return 0; ++} ++ ++ ++/*check for gnutella get commands and other typical data*/ ++int ++search_all_gnu (unsigned char *haystack, int packet_len, int head_len) ++{ ++ unsigned char *t = haystack; ++ int c; ++ ++ if (!((*(haystack + packet_len - 2) == 0x0d) && (*(haystack + packet_len - 1) == 0x0a))) return 0; ++ ++ t += head_len; ++ ++ if (memcmp(t, "GNUTELLA CONNECT/", 17) == 0) return ((IPP2P_GNU * 100) + 1); ++ if (memcmp(t, "GNUTELLA/", 9) == 0) return ((IPP2P_GNU * 100) + 2); ++ ++ if ((memcmp(t, "GET /get/", 9) == 0) || (memcmp(t, "GET /uri-res/", 13) == 0)) ++ { ++ c = head_len + 8; ++ t += 8; ++ while (c < packet_len - 22) { ++ if ((t[0] == 0x0d) && (t[1] == 0x0a)) { ++ t += 2; ++ c += 2; ++ if ((memcmp(t, "X-Gnutella-", 11) == 0) || (memcmp(t, "X-Queue:", 8) == 0)) return ((IPP2P_GNU * 100) + 3); ++ } else { ++ t++; ++ c++; ++ } ++ } ++ } ++ return 0; ++} ++ ++ ++/*check for KaZaA download commands and other typical data*/ ++int ++search_all_kazaa (unsigned char *haystack, int packet_len, int head_len) ++{ ++ unsigned char *t = haystack; ++ int c; ++ ++ if (!((*(haystack + packet_len - 2) == 0x0d) && (*(haystack + packet_len - 1) == 0x0a))) return 0; ++ ++ t += head_len; ++ if (memcmp(t, "GIVE ", 5) == 0) return ((IPP2P_KAZAA * 100) + 1); ++ ++ if (memcmp(t, "GET /", 5) == 0) { ++ c = head_len + 8; ++ t += 8; ++ while (c < packet_len - 22) { ++ if ((t[0] == 0x0d) && (t[1] == 0x0a)) { ++ t += 2; ++ c += 2; ++ if ( memcmp(t, "X-Kazaa-Username: ", 18) == 0 ) return ((IPP2P_KAZAA * 100) + 2); ++ if ( memcmp(t, "User-Agent: PeerEnabler/", 24) == 0 ) return ((IPP2P_KAZAA * 100) + 3); ++ } else { ++ t++; ++ c++; ++ } ++ } ++ } ++ ++ return 0; ++} ++ ++/*fast check for edonkey file segment transfer command*/ ++int ++search_edk (unsigned char *haystack, int packet_len, int head_len) ++{ ++ if (*(haystack+head_len) != 0xe3) ++ return 0; ++ else { ++ if (*(haystack+head_len+5) == 0x47) ++ return (IPP2P_DATA_EDK * 100); ++ else ++ return 0; ++ } ++} ++ ++ ++ ++/*intensive but slower search for some edonkey packets including size-check*/ ++int ++search_all_edk (unsigned char *haystack, int packet_len, int head_len) ++{ ++ unsigned char *t = haystack; ++ int cmd; ++ ++ if (*(haystack+head_len) == 0xd4) { ++ t += head_len; ++ cmd = get_u16(t, 1); ++ if (cmd == (packet_len - head_len - 5)) { ++ switch (t[5]) { ++ case 0x82: return ((IPP2P_EDK * 100) + 42); ++ case 0x15: return ((IPP2P_EDK * 100) + 43); ++ default: return 0; ++ } ++ } ++ return 0; ++ } ++ ++ ++ if (*(haystack+head_len) == 0xc5) { /*search for additional eMule packets*/ ++ t += head_len; ++ cmd = get_u16(t, 1); ++ ++ if (cmd == (packet_len - head_len - 5)) { ++ switch (t[5]) { ++ case 0x01: return ((IPP2P_EDK * 100) + 30); ++ case 0x02: return ((IPP2P_EDK * 100) + 31); ++ case 0x60: return ((IPP2P_EDK * 100) + 32); ++ case 0x81: return ((IPP2P_EDK * 100) + 33); ++ case 0x82: return ((IPP2P_EDK * 100) + 34); ++ case 0x85: return ((IPP2P_EDK * 100) + 35); ++ case 0x86: return ((IPP2P_EDK * 100) + 36); ++ case 0x87: return ((IPP2P_EDK * 100) + 37); ++ case 0x40: return ((IPP2P_EDK * 100) + 38); ++ case 0x92: return ((IPP2P_EDK * 100) + 39); ++ case 0x93: return ((IPP2P_EDK * 100) + 40); ++ case 0x12: return ((IPP2P_EDK * 100) + 41); ++ default: return 0; ++ } ++ } ++ ++ return 0; ++ } ++ ++ ++ if (*(haystack+head_len) != 0xe3) ++ return 0; ++ else { ++ t += head_len; ++ cmd = get_u16(t, 1); ++ if (cmd == (packet_len - head_len - 5)) { ++ switch (t[5]) { ++ case 0x01: return ((IPP2P_EDK * 100) + 1); /*Client: hello or Server:hello*/ ++ case 0x50: return ((IPP2P_EDK * 100) + 2); /*Client: file status*/ ++ case 0x16: return ((IPP2P_EDK * 100) + 3); /*Client: search*/ ++ case 0x58: return ((IPP2P_EDK * 100) + 4); /*Client: file request*/ ++ case 0x48: return ((IPP2P_EDK * 100) + 5); /*???*/ ++ case 0x54: return ((IPP2P_EDK * 100) + 6); /*???*/ ++ case 0x47: return ((IPP2P_EDK * 100) + 7); /*Client: file segment request*/ ++ case 0x46: return ((IPP2P_EDK * 100) + 8); /*Client: download segment*/ ++ case 0x4c: return ((IPP2P_EDK * 100) + 9); /*Client: Hello-Answer*/ ++ case 0x4f: return ((IPP2P_EDK * 100) + 10); /*Client: file status request*/ ++ case 0x59: return ((IPP2P_EDK * 100) + 11); /*Client: file request answer*/ ++ case 0x65: return ((IPP2P_EDK * 100) + 12); /*Client: ???*/ ++ case 0x66: return ((IPP2P_EDK * 100) + 13); /*Client: ???*/ ++ case 0x51: return ((IPP2P_EDK * 100) + 14); /*Client: ???*/ ++ case 0x52: return ((IPP2P_EDK * 100) + 15); /*Client: ???*/ ++ case 0x4d: return ((IPP2P_EDK * 100) + 16); /*Client: ???*/ ++ case 0x5c: return ((IPP2P_EDK * 100) + 17); /*Client: ???*/ ++ case 0x38: return ((IPP2P_EDK * 100) + 18); /*Client: ???*/ ++ case 0x69: return ((IPP2P_EDK * 100) + 19); /*Client: ???*/ ++ case 0x19: return ((IPP2P_EDK * 100) + 20); /*Client: ???*/ ++ case 0x42: return ((IPP2P_EDK * 100) + 21); /*Client: ???*/ ++ case 0x34: return ((IPP2P_EDK * 100) + 22); /*Client: ???*/ ++ case 0x94: return ((IPP2P_EDK * 100) + 23); /*Client: ???*/ ++ case 0x1c: return ((IPP2P_EDK * 100) + 24); /*Client: ???*/ ++ case 0x6a: return ((IPP2P_EDK * 100) + 25); /*Client: ???*/ ++ default: return 0; ++ } ++ } else { ++ if (cmd > packet_len - head_len - 5) { ++ if ((t[3] == 0x00) && (t[4] == 0x00)) { ++ if (t[5] == 0x01) return ((IPP2P_EDK * 100) + 26); ++ if (t[5] == 0x4c) return ((IPP2P_EDK * 100) + 27); ++ } ++ return 0; ++ ++ } /*non edk packet*/ ++ if (t[cmd+5] == 0xe3) return ((IPP2P_EDK * 100) + 28);/*found another edk-command*/ ++ if (t[cmd+5] == 0xc5) return ((IPP2P_EDK * 100) + 29);/*found an emule-command*/ ++ return 0; ++ } ++ } ++} ++ ++ ++/*fast check for Direct Connect send command*/ ++int ++search_dc (unsigned char *haystack, int packet_len, int head_len) ++{ ++ unsigned char *t = haystack; ++ ++ if (*(haystack+head_len) != 0x24 ) ++ return 0; ++ else { ++ t += head_len + 1; ++ if (memcmp(t, "Send|", 5) == 0) ++ return (IPP2P_DATA_DC * 100); ++ else ++ return 0; ++ } ++ ++} ++ ++ ++/*intensive but slower check for all direct connect packets*/ ++int ++search_all_dc (unsigned char *haystack, int packet_len, int head_len) ++{ ++ unsigned char *t = haystack; ++ ++ if ((*(haystack + head_len) == 0x24) && (*(haystack + packet_len - 1) == 0x7c)) { ++ t += head_len + 1; ++ if (memcmp(t, "Lock ", 5) == 0) return ((IPP2P_DC * 100) + 1); /*hub: hello*/ ++ if (memcmp(t, "Key ", 4) == 0) return ((IPP2P_DC * 100) + 2); /*client: hello*/ ++ if (memcmp(t, "Hello ", 6) == 0) return ((IPP2P_DC * 100) + 3); /*hub:connected*/ ++ if (memcmp(t, "MyNick ", 7) == 0) return ((IPP2P_DC * 100) + 4); /*client-client: hello*/ ++ if (memcmp(t, "Search ", 7) == 0) return ((IPP2P_DC * 100) + 5); /*client: search*/ ++ if (memcmp(t, "Send", 4) == 0) return ((IPP2P_DC * 100) + 6); /*client: start download*/ ++ return 0; ++ } else ++ return 0; ++} ++ ++ ++static struct { ++ int command; ++ __u8 short_hand; /*for fucntions included in short hands*/ ++ int packet_len; ++ int (*function_name) (unsigned char *, int, int); ++} matchlist[] = { ++ {IPP2P_EDK,SHORT_HAND_IPP2P,40, &search_all_edk}, ++ {IPP2P_DATA_KAZAA,SHORT_HAND_DATA,200, &search_kazaa}, ++ {IPP2P_DATA_EDK,SHORT_HAND_DATA,60, &search_edk}, ++ {IPP2P_DATA_DC,SHORT_HAND_DATA,26, &search_dc}, ++ {IPP2P_DC,SHORT_HAND_IPP2P,25, search_all_dc}, ++ {IPP2P_DATA_GNU,SHORT_HAND_DATA,40, &search_gnu}, ++ {IPP2P_GNU,SHORT_HAND_IPP2P,35, &search_all_gnu}, ++ {IPP2P_KAZAA,SHORT_HAND_IPP2P,35, &search_all_kazaa}, ++ {IPP2P_BIT,SHORT_HAND_NONE,40, &search_bittorrent}, ++ {IPP2P_APPLE,SHORT_HAND_NONE,20, &search_apple}, ++ {IPP2P_SOUL,SHORT_HAND_NONE,25, &search_soul}, ++ {IPP2P_WINMX,SHORT_HAND_NONE,20, &search_winmx}, ++ {IPP2P_ARES,SHORT_HAND_NONE,25, &search_ares}, ++ {0,0,0,NULL} ++}; ++ ++ ++static struct { ++ int command; ++ __u8 short_hand; /*for fucntions included in short hands*/ ++ int packet_len; ++ int (*function_name) (unsigned char *, int); ++} udp_list[] = { ++ {IPP2P_KAZAA,SHORT_HAND_IPP2P,14, &udp_search_kazaa}, ++ {IPP2P_BIT,SHORT_HAND_NONE,23, &udp_search_bit}, ++ {IPP2P_GNU,SHORT_HAND_IPP2P,11, &udp_search_gnu}, ++ {IPP2P_EDK,SHORT_HAND_IPP2P,9, &udp_search_edk}, ++ {0,0,0,NULL} ++}; ++ ++ ++static int ++match(const struct sk_buff *skb, ++ const struct net_device *in, ++ const struct net_device *out, ++ const void *matchinfo, ++ int offset, ++ ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0) ++ const void *hdr, ++ u_int16_t datalen, ++#endif ++ ++ int *hotdrop) ++{ ++ const struct ipt_p2p_info *info = matchinfo; ++ unsigned char *haystack; ++ struct iphdr *ip = skb->nh.iph; ++ int p2p_result = 0, i = 0; ++ int head_len; ++ int hlen = ntohs(ip->tot_len)-(ip->ihl*4); /*hlen = packet-data length*/ ++ ++ /*must not be a fragment*/ ++ if (offset) { ++ if (info->debug) printk("IPP2P.match: offset found %i \n",offset); ++ return 0; ++ } ++ ++ /*make sure that skb is linear*/ ++ if(skb_is_nonlinear(skb)){ ++ if (info->debug) printk("IPP2P.match: nonlinear skb found\n"); ++ return 0; ++ } ++ ++ ++ haystack=(char *)ip+(ip->ihl*4); /*haystack = packet data*/ ++ ++ switch (ip->protocol){ ++ case IPPROTO_TCP: /*what to do with a TCP packet*/ ++ { ++ struct tcphdr *tcph = (void *) ip + ip->ihl * 4; ++ ++ if (tcph->fin) return 0; /*if FIN bit is set bail out*/ ++ if (tcph->syn) return 0; /*if SYN bit is set bail out*/ ++ if (tcph->rst) return 0; /*if RST bit is set bail out*/ ++ head_len = tcph->doff * 4; /*get TCP-Header-Size*/ ++ while (matchlist[i].command) { ++ if ((((info->cmd & matchlist[i].command) == matchlist[i].command) || ++ ((info->cmd & matchlist[i].short_hand) == matchlist[i].short_hand)) && ++ (hlen > matchlist[i].packet_len)) { ++ p2p_result = matchlist[i].function_name(haystack, hlen, head_len); ++ if (p2p_result) ++ { ++ if (info->debug) printk("IPP2P.debug:TCP-match: %i from: %u.%u.%u.%u:%i to: %u.%u.%u.%u:%i Length: %i\n", ++ p2p_result, NIPQUAD(ip->saddr),ntohs(tcph->source), NIPQUAD(ip->daddr),ntohs(tcph->dest),hlen); ++ return p2p_result; ++ } ++ } ++ i++; ++ } ++ return p2p_result; ++ } ++ ++ case IPPROTO_UDP: /*what to do with an UDP packet*/ ++ { ++ struct udphdr *udph = (void *) ip + ip->ihl * 4; ++ ++ while (udp_list[i].command){ ++ if ((((info->cmd & udp_list[i].command) == udp_list[i].command) || ++ ((info->cmd & udp_list[i].short_hand) == udp_list[i].short_hand)) && ++ (hlen > udp_list[i].packet_len)) { ++ p2p_result = udp_list[i].function_name(haystack, hlen); ++ if (p2p_result){ ++ if (info->debug) printk("IPP2P.debug:UDP-match: %i from: %u.%u.%u.%u:%i to: %u.%u.%u.%u:%i Length: %i\n", ++ p2p_result, NIPQUAD(ip->saddr),ntohs(udph->source), NIPQUAD(ip->daddr),ntohs(udph->dest),hlen); ++ return p2p_result; ++ } ++ } ++ i++; ++ } ++ return p2p_result; ++ } ++ ++ default: return 0; ++ } ++} ++ ++ ++ ++static int ++checkentry(const char *tablename, ++ const struct ipt_ip *ip, ++ void *matchinfo, ++ unsigned int matchsize, ++ unsigned int hook_mask) ++{ ++ /* Must specify -p tcp */ ++/* if (ip->proto != IPPROTO_TCP || (ip->invflags & IPT_INV_PROTO)) { ++ * printk("ipp2p: Only works on TCP packets, use -p tcp\n"); ++ * return 0; ++ * }*/ ++ return 1; ++} ++ ++ ++ ++ ++static struct ipt_match ipp2p_match = { ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0) ++ { NULL, NULL }, ++ "ipp2p", ++ &match, ++ &checkentry, ++ NULL, ++ THIS_MODULE ++#endif ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) ++ .name = "ipp2p", ++ .match = &match, ++ .checkentry = &checkentry, ++ .me = THIS_MODULE, ++#endif ++}; ++ ++ ++static int __init init(void) ++{ ++ printk(KERN_INFO "IPP2P v%s loading\n", IPP2P_VERSION); ++ return ipt_register_match(&ipp2p_match); ++} ++ ++static void __exit fini(void) ++{ ++ ipt_unregister_match(&ipp2p_match); ++ printk(KERN_INFO "IPP2P v%s unloaded\n", IPP2P_VERSION); ++} ++ ++module_init(init); ++module_exit(fini); ++ ++ +diff -urN linux-2.4.29.old/net/ipv4/netfilter/Makefile linux-2.4.29/net/ipv4/netfilter/Makefile +--- linux-2.4.29.old/net/ipv4/netfilter/Makefile 2005-03-12 00:40:38.000000000 +0100 ++++ linux-2.4.29/net/ipv4/netfilter/Makefile 2005-03-12 00:42:57.000000000 +0100 +@@ -67,6 +67,7 @@ + obj-$(CONFIG_IP_NF_MATCH_LIMIT) += ipt_limit.o + obj-$(CONFIG_IP_NF_MATCH_MARK) += ipt_mark.o + obj-$(CONFIG_IP_NF_MATCH_MAC) += ipt_mac.o ++obj-$(CONFIG_IP_NF_MATCH_IPP2P) += ipt_ipp2p.o + + obj-$(CONFIG_IP_NF_MATCH_PKTTYPE) += ipt_pkttype.o + obj-$(CONFIG_IP_NF_MATCH_MULTIPORT) += ipt_multiport.o diff --git a/openwrt/target/linux/linux-2.4/patches/generic/602-netfilter_layer7.patch b/openwrt/target/linux/linux-2.4/patches/generic/602-netfilter_layer7.patch new file mode 100644 index 000000000..55e817aa4 --- /dev/null +++ b/openwrt/target/linux/linux-2.4/patches/generic/602-netfilter_layer7.patch @@ -0,0 +1,2050 @@ +diff -Nurp linux-2.4.30/Documentation/Configure.help linux-2.4.30-layer7/Documentation/Configure.help +--- linux-2.4.30/Documentation/Configure.help 2005-04-03 20:42:19.000000000 -0500 ++++ linux-2.4.30-layer7/Documentation/Configure.help 2005-05-03 18:37:03.000000000 -0500 +@@ -29056,6 +29056,23 @@ CONFIG_SOUND_WM97XX + + If unsure, say N. + ++CONFIG_IP_NF_MATCH_LAYER7 ++ Say Y if you want to be able to classify connections (and their ++ packets) based on regular expression matching of their application ++ layer data. This is one way to classify applications such as ++ peer-to-peer filesharing systems that do not always use the same ++ port. ++ ++ To compile it as a module, choose M here. If unsure, say N. ++ ++CONFIG_IP_NF_MATCH_LAYER7_DEBUG ++ Say Y to get lots of debugging output. ++ ++CONFIG_IP_NF_MATCH_LAYER7_MAXDATALEN ++ Size of the buffer that the application layer data is stored in. ++ Unless you know what you're doing, leave it at the default of 2048 ++ Bytes. ++ + # + # A couple of things I keep forgetting: + # capitalize: AppleTalk, Ethernet, DOS, DMA, FAT, FTP, Internet, +diff -Nurp linux-2.4.30/include/linux/netfilter_ipv4/ip_conntrack.h linux-2.4.30-layer7/include/linux/netfilter_ipv4/ip_conntrack.h +--- linux-2.4.30/include/linux/netfilter_ipv4/ip_conntrack.h 2005-04-03 20:42:20.000000000 -0500 ++++ linux-2.4.30-layer7/include/linux/netfilter_ipv4/ip_conntrack.h 2005-05-03 18:37:03.000000000 -0500 +@@ -207,6 +207,17 @@ struct ip_conntrack + } nat; + #endif /* CONFIG_IP_NF_NAT_NEEDED */ + ++#if defined(CONFIG_IP_NF_MATCH_LAYER7) || defined(CONFIG_IP_NF_MATCH_LAYER7_MODULE) ++ struct { ++ unsigned int numpackets; /* surely this is kept track of somewhere else, right? I can't find it... */ ++ char * app_proto; /* "http", "ftp", etc. NULL if unclassifed */ ++ ++ /* the application layer data so far. NULL if ->numpackets > numpackets */ ++ char * app_data; ++ ++ unsigned int app_data_len; ++ } layer7; ++#endif + }; + + /* get master conntrack via master expectation */ +diff -Nurp linux-2.4.30/include/linux/netfilter_ipv4/ipt_layer7.h linux-2.4.30-layer7/include/linux/netfilter_ipv4/ipt_layer7.h +--- linux-2.4.30/include/linux/netfilter_ipv4/ipt_layer7.h 1969-12-31 18:00:00.000000000 -0600 ++++ linux-2.4.30-layer7/include/linux/netfilter_ipv4/ipt_layer7.h 2005-05-03 18:37:03.000000000 -0500 +@@ -0,0 +1,26 @@ ++/* ++ By Matthew Strait <quadong@users.sf.net>, Dec 2003. ++ http://l7-filter.sf.net ++ ++ 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. ++ http://www.gnu.org/licenses/gpl.txt ++*/ ++ ++#ifndef _IPT_LAYER7_H ++#define _IPT_LAYER7_H ++ ++#define MAX_PATTERN_LEN 8192 ++#define MAX_PROTOCOL_LEN 256 ++ ++typedef char *(*proc_ipt_search) (char *, char, char *); ++ ++struct ipt_layer7_info { ++ char protocol[MAX_PROTOCOL_LEN]; ++ char invert:1; ++ char pattern[MAX_PATTERN_LEN]; ++}; ++ ++#endif /* _IPT_LAYER7_H */ +diff -Nurp linux-2.4.30/net/ipv4/netfilter/Config.in linux-2.4.30-layer7/net/ipv4/netfilter/Config.in +--- linux-2.4.30/net/ipv4/netfilter/Config.in 2005-01-19 08:10:13.000000000 -0600 ++++ linux-2.4.30-layer7/net/ipv4/netfilter/Config.in 2005-05-03 18:37:03.000000000 -0500 +@@ -43,6 +43,10 @@ if [ "$CONFIG_IP_NF_IPTABLES" != "n" ]; + if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then + dep_tristate ' Unclean match support (EXPERIMENTAL)' CONFIG_IP_NF_MATCH_UNCLEAN $CONFIG_IP_NF_IPTABLES + dep_tristate ' Owner match support (EXPERIMENTAL)' CONFIG_IP_NF_MATCH_OWNER $CONFIG_IP_NF_IPTABLES ++ dep_tristate ' Layer 7 match support (EXPERIMENTAL)' CONFIG_IP_NF_MATCH_LAYER7 $CONFIG_IP_NF_CONNTRACK ++ dep_mbool ' Layer 7 debugging output (EXPERIMENTAL)' CONFIG_IP_NF_MATCH_LAYER7_DEBUG $CONFIG_IP_NF_MATCH_LAYER7 ++ int ' Buffer size for application layer data (256-65536)' CONFIG_IP_NF_MATCH_LAYER7_MAXDATALEN 2048 ++ + fi + # The targets + dep_tristate ' Packet filtering' CONFIG_IP_NF_FILTER $CONFIG_IP_NF_IPTABLES +diff -Nurp linux-2.4.30/net/ipv4/netfilter/Makefile linux-2.4.30-layer7/net/ipv4/netfilter/Makefile +--- linux-2.4.30/net/ipv4/netfilter/Makefile 2003-08-25 06:44:44.000000000 -0500 ++++ linux-2.4.30-layer7/net/ipv4/netfilter/Makefile 2005-05-03 18:44:12.000000000 -0500 +@@ -86,6 +86,7 @@ obj-$(CONFIG_IP_NF_MATCH_STATE) += ipt_s + obj-$(CONFIG_IP_NF_MATCH_CONNTRACK) += ipt_conntrack.o + obj-$(CONFIG_IP_NF_MATCH_UNCLEAN) += ipt_unclean.o + obj-$(CONFIG_IP_NF_MATCH_TCPMSS) += ipt_tcpmss.o ++obj-$(CONFIG_IP_NF_MATCH_LAYER7) += ipt_layer7.o + + # targets + obj-$(CONFIG_IP_NF_TARGET_REJECT) += ipt_REJECT.o +diff -Nurp linux-2.4.30/net/ipv4/netfilter/ip_conntrack_core.c linux-2.4.30-layer7/net/ipv4/netfilter/ip_conntrack_core.c +--- linux-2.4.30/net/ipv4/netfilter/ip_conntrack_core.c 2005-04-03 20:42:20.000000000 -0500 ++++ linux-2.4.30-layer7/net/ipv4/netfilter/ip_conntrack_core.c 2005-05-03 18:37:03.000000000 -0500 +@@ -346,6 +346,14 @@ destroy_conntrack(struct nf_conntrack *n + } + kfree(ct->master); + } ++ ++ #if defined(CONFIG_IP_NF_MATCH_LAYER7) || defined(CONFIG_IP_NF_MATCH_LAYER7_MODULE) ++ if(ct->layer7.app_proto) ++ kfree(ct->layer7.app_proto); ++ if(ct->layer7.app_data) ++ kfree(ct->layer7.app_data); ++ #endif ++ + WRITE_UNLOCK(&ip_conntrack_lock); + + if (master) +diff -Nurp linux-2.4.30/net/ipv4/netfilter/ip_conntrack_standalone.c linux-2.4.30-layer7/net/ipv4/netfilter/ip_conntrack_standalone.c +--- linux-2.4.30/net/ipv4/netfilter/ip_conntrack_standalone.c 2005-04-03 20:42:20.000000000 -0500 ++++ linux-2.4.30-layer7/net/ipv4/netfilter/ip_conntrack_standalone.c 2005-05-03 18:37:03.000000000 -0500 +@@ -107,6 +107,13 @@ print_conntrack(char *buffer, struct ip_ + len += sprintf(buffer + len, "[ASSURED] "); + len += sprintf(buffer + len, "use=%u ", + atomic_read(&conntrack->ct_general.use)); ++ ++ #if defined(CONFIG_IP_NF_MATCH_LAYER7) || defined(CONFIG_IP_NF_MATCH_LAYER7_MODULE) ++ if(conntrack->layer7.app_proto) ++ len += sprintf(buffer + len, "l7proto=%s ", ++ conntrack->layer7.app_proto); ++ #endif ++ + len += sprintf(buffer + len, "\n"); + + return len; +diff -Nurp linux-2.4.30/net/ipv4/netfilter/ipt_layer7.c linux-2.4.30-layer7/net/ipv4/netfilter/ipt_layer7.c +--- linux-2.4.30/net/ipv4/netfilter/ipt_layer7.c 1969-12-31 18:00:00.000000000 -0600 ++++ linux-2.4.30-layer7/net/ipv4/netfilter/ipt_layer7.c 2005-05-03 18:37:03.000000000 -0500 +@@ -0,0 +1,557 @@ ++/* ++ Kernel module to match application layer (OSI layer 7) ++ data in connections. ++ ++ http://l7-filter.sf.net ++ ++ By Matthew Strait and Ethan Sommer, 2003-2005. ++ ++ 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. ++ http://www.gnu.org/licenses/gpl.txt ++ ++ Based on ipt_string.c (C) 2000 Emmanuel Roger <winfield@freegates.be> ++ and cls_layer7.c (C) 2003 Matthew Strait, Ethan Sommer, Justin Levandoski ++*/ ++ ++#include <linux/module.h> ++#include <linux/skbuff.h> ++#include <linux/netfilter_ipv4/ip_conntrack.h> ++#include <linux/proc_fs.h> ++#include <linux/ctype.h> ++#include <net/ip.h> ++#include <net/tcp.h> ++#include <linux/netfilter_ipv4/lockhelp.h> ++ ++#include "regexp/regexp.c" ++ ++#include <linux/netfilter_ipv4/ipt_layer7.h> ++#include <linux/netfilter_ipv4/ip_tables.h> ++ ++MODULE_AUTHOR("Matthew Strait <quadong@users.sf.net>, Ethan Sommer <sommere@users.sf.net>"); ++MODULE_LICENSE("GPL"); ++MODULE_DESCRIPTION("iptables application layer match module"); ++ ++#if defined(CONFIG_IP_NF_MATCH_LAYER7_DEBUG) ++ #define DPRINTK(format,args...) printk(format,##args) ++#else ++ #define DPRINTK(format,args...) ++#endif ++ ++#define TOTAL_PACKETS master_conntrack->layer7.numpackets ++ ++/* Number of packets whose data we look at. ++This can be modified through /proc/net/layer7_numpackets */ ++static int num_packets = 8; ++ ++static struct pattern_cache { ++ char * regex_string; ++ regexp * pattern; ++ struct pattern_cache * next; ++} * first_pattern_cache = NULL; ++ ++/* I'm new to locking. Here are my assumptions: ++ ++- No one will write to /proc/net/layer7_numpackets over and over very fast; ++ if they did, nothing awful would happen. ++ ++- This code will never be processing the same packet twice at the same time, ++ because iptables rules are traversed in order. ++ ++- It doesn't matter if two packets from different connections are in here at ++ the same time, because they don't share any data. ++ ++- It _does_ matter if two packets from the same connection are here at the same ++ time. In this case, we have to protect the conntracks and the list of ++ compiled patterns. ++*/ ++DECLARE_RWLOCK(ct_lock); ++DECLARE_LOCK(list_lock); ++ ++#if CONFIG_IP_NF_MATCH_LAYER7_DEBUG ++/* Converts an unfriendly string into a friendly one by ++replacing unprintables with periods and all whitespace with " ". */ ++static char * friendly_print(unsigned char * s) ++{ ++ char * f = kmalloc(strlen(s) + 1, GFP_ATOMIC); ++ int i; ++ ++ if(!f) { ++ if (net_ratelimit()) ++ printk(KERN_ERR "layer7: out of memory in friendly_print, bailing.\n"); ++ return NULL; ++ } ++ ++ for(i = 0; i < strlen(s); i++){ ++ if(isprint(s[i]) && s[i] < 128) f[i] = s[i]; ++ else if(isspace(s[i])) f[i] = ' '; ++ else f[i] = '.'; ++ } ++ f[i] = '\0'; ++ return f; ++} ++ ++static char dec2hex(int i) ++{ ++ switch (i) { ++ case 0 ... 9: ++ return (char)(i + '0'); ++ break; ++ case 10 ... 15: ++ return (char)(i - 10 + 'a'); ++ break; ++ default: ++ if (net_ratelimit()) ++ printk("Problem in dec2hex\n"); ++ return '\0'; ++ } ++} ++ ++static char * hex_print(unsigned char * s) ++{ ++ char * g = kmalloc(strlen(s)*3 + 1, GFP_ATOMIC); ++ int i; ++ ++ if(!g) { ++ if (net_ratelimit()) ++ printk(KERN_ERR "layer7: out of memory in hex_print, bailing.\n"); ++ return NULL; ++ } ++ ++ for(i = 0; i < strlen(s); i++) { ++ g[i*3 ] = dec2hex(s[i]/16); ++ g[i*3 + 1] = dec2hex(s[i]%16); ++ g[i*3 + 2] = ' '; ++ } ++ g[i*3] = '\0'; ++ ++ return g; ++} ++#endif // DEBUG ++ ++/* Use instead of regcomp. As we expect to be seeing the same regexps over and ++over again, it make sense to cache the results. */ ++static regexp * compile_and_cache(char * regex_string, char * protocol) ++{ ++ struct pattern_cache * node = first_pattern_cache; ++ struct pattern_cache * last_pattern_cache = first_pattern_cache; ++ struct pattern_cache * tmp; ++ unsigned int len; ++ ++ while (node != NULL) { ++ if (!strcmp(node->regex_string, regex_string)) ++ return node->pattern; ++ ++ last_pattern_cache = node;/* points at the last non-NULL node */ ++ node = node->next; ++ } ++ ++ /* If we reach the end of the list, then we have not yet cached ++ the pattern for this regex. Let's do that now. ++ Be paranoid about running out of memory to avoid list corruption. */ ++ tmp = kmalloc(sizeof(struct pattern_cache), GFP_ATOMIC); ++ ++ if(!tmp) { ++ if (net_ratelimit()) ++ printk(KERN_ERR "layer7: out of memory in compile_and_cache, bailing.\n"); ++ return NULL; ++ } ++ ++ tmp->regex_string = kmalloc(strlen(regex_string) + 1, GFP_ATOMIC); ++ tmp->pattern = kmalloc(sizeof(struct regexp), GFP_ATOMIC); ++ tmp->next = NULL; ++ ++ if(!tmp->regex_string || !tmp->pattern) { ++ if (net_ratelimit()) ++ printk(KERN_ERR "layer7: out of memory in compile_and_cache, bailing.\n"); ++ kfree(tmp->regex_string); ++ kfree(tmp->pattern); ++ kfree(tmp); ++ return NULL; ++ } ++ ++ /* Ok. The new node is all ready now. */ ++ node = tmp; ++ ++ if(first_pattern_cache == NULL) /* list is empty */ ++ first_pattern_cache = node; /* make node the beginning */ ++ else ++ last_pattern_cache->next = node; /* attach node to the end */ ++ ++ /* copy the string and compile the regex */ ++ len = strlen(regex_string); ++ DPRINTK("About to compile this: \"%s\"\n", regex_string); ++ node->pattern = regcomp(regex_string, &len); ++ if ( !node->pattern ) { ++ if (net_ratelimit()) ++ printk(KERN_ERR "layer7: Error compiling regexp \"%s\" (%s)\n", regex_string, protocol); ++ /* pattern is now cached as NULL, so we won't try again. */ ++ } ++ ++ strcpy(node->regex_string, regex_string); ++ return node->pattern; ++} ++ ++static int can_handle(const struct sk_buff *skb) ++{ ++ if(!skb->nh.iph) /* not IP */ ++ return 0; ++ if(skb->nh.iph->protocol != IPPROTO_TCP && ++ skb->nh.iph->protocol != IPPROTO_UDP && ++ skb->nh.iph->protocol != IPPROTO_ICMP) ++ return 0; ++ return 1; ++} ++ ++/* Returns offset the into the skb->data that the application data starts */ ++static int app_data_offset(const struct sk_buff *skb) ++{ ++ /* In case we are ported somewhere (ebtables?) where skb->nh.iph ++ isn't set, this can be gotten from 4*(skb->data[0] & 0x0f) as well. */ ++ int ip_hl = 4*skb->nh.iph->ihl; ++ ++ if( skb->nh.iph->protocol == IPPROTO_TCP ) { ++ /* 12 == offset into TCP header for the header length field. ++ Can't get this with skb->h.th->doff because the tcphdr ++ struct doesn't get set when routing (this is confirmed to be ++ true in Netfilter as well as QoS.) */ ++ int tcp_hl = 4*(skb->data[ip_hl + 12] >> 4); ++ ++ return ip_hl + tcp_hl; ++ } else if( skb->nh.iph->protocol == IPPROTO_UDP ) { ++ return ip_hl + 8; /* UDP header is always 8 bytes */ ++ } else if( skb->nh.iph->protocol == IPPROTO_ICMP ) { ++ return ip_hl + 8; /* ICMP header is 8 bytes */ ++ } else { ++ if (net_ratelimit()) ++ printk(KERN_ERR "layer7: tried to handle unknown protocol!\n"); ++ return ip_hl + 8; /* something reasonable */ ++ } ++} ++ ++/* handles whether there's a match when we aren't appending data anymore */ ++static int match_no_append(struct ip_conntrack * conntrack, struct ip_conntrack * master_conntrack, ++ enum ip_conntrack_info ctinfo, enum ip_conntrack_info master_ctinfo, ++ struct ipt_layer7_info * info) ++{ ++ /* If we're in here, throw the app data away */ ++ WRITE_LOCK(&ct_lock); ++ if(master_conntrack->layer7.app_data != NULL) { ++ ++ #ifdef CONFIG_IP_NF_MATCH_LAYER7_DEBUG ++ if(!master_conntrack->layer7.app_proto) { ++ char * f = friendly_print(master_conntrack->layer7.app_data); ++ char * g = hex_print(master_conntrack->layer7.app_data); ++ DPRINTK("\nl7-filter gave up after %d bytes (%d packets):\n%s\n", ++ strlen(f), ++ TOTAL_PACKETS, f); ++ kfree(f); ++ DPRINTK("In hex: %s\n", g); ++ kfree(g); ++ } ++ #endif ++ ++ kfree(master_conntrack->layer7.app_data); ++ master_conntrack->layer7.app_data = NULL; /* don't free again */ ++ } ++ WRITE_UNLOCK(&ct_lock); ++ ++ if(master_conntrack->layer7.app_proto){ ++ /* Here child connections set their .app_proto (for /proc/net/ip_conntrack) */ ++ WRITE_LOCK(&ct_lock); ++ if(!conntrack->layer7.app_proto) { ++ conntrack->layer7.app_proto = kmalloc(strlen(master_conntrack->layer7.app_proto)+1, GFP_ATOMIC); ++ if(!conntrack->layer7.app_proto){ ++ if (net_ratelimit()) ++ printk(KERN_ERR "layer7: out of memory in match_no_append, bailing.\n"); ++ WRITE_UNLOCK(&ct_lock); ++ return 1; ++ } ++ strcpy(conntrack->layer7.app_proto, master_conntrack->layer7.app_proto); ++ } ++ WRITE_UNLOCK(&ct_lock); ++ ++ return (!strcmp(master_conntrack->layer7.app_proto, info->protocol)); ++ } ++ else { ++ /* If not classified, set to "unknown" to distinguish from ++ connections that are still being tested. */ ++ WRITE_LOCK(&ct_lock); ++ master_conntrack->layer7.app_proto = kmalloc(strlen("unknown")+1, GFP_ATOMIC); ++ if(!master_conntrack->layer7.app_proto){ ++ if (net_ratelimit()) ++ printk(KERN_ERR "layer7: out of memory in match_no_append, bailing.\n"); ++ WRITE_UNLOCK(&ct_lock); ++ return 1; ++ } ++ strcpy(master_conntrack->layer7.app_proto, "unknown"); ++ WRITE_UNLOCK(&ct_lock); ++ return 0; ++ } ++} ++ ++/* add the new app data to the conntrack. Return number of bytes added. */ ++static int add_data(struct ip_conntrack * master_conntrack, ++ char * app_data, int appdatalen) ++{ ++ int length = 0, i; ++ int oldlength = master_conntrack->layer7.app_data_len; ++ ++ /* Strip nulls. Make everything lower case (our regex lib doesn't ++ do case insensitivity). Add it to the end of the current data. */ ++ for(i = 0; i < CONFIG_IP_NF_MATCH_LAYER7_MAXDATALEN-oldlength-1 && ++ i < appdatalen; i++) { ++ if(app_data[i] != '\0') { ++ master_conntrack->layer7.app_data[length+oldlength] = ++ /* the kernel version of tolower mungs 'upper ascii' */ ++ isascii(app_data[i])? tolower(app_data[i]) : app_data[i]; ++ length++; ++ } ++ } ++ ++ master_conntrack->layer7.app_data[length+oldlength] = '\0'; ++ master_conntrack->layer7.app_data_len = length + oldlength; ++ ++ return length; ++} ++ ++/* Returns true on match and false otherwise. */ ++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) ++{ ++ struct ipt_layer7_info * info = (struct ipt_layer7_info *)matchinfo; ++ enum ip_conntrack_info master_ctinfo, ctinfo; ++ struct ip_conntrack *master_conntrack, *conntrack; ++ unsigned char * app_data; ++ unsigned int pattern_result, appdatalen; ++ regexp * comppattern; ++ ++ if(!can_handle(skb)){ ++ DPRINTK("layer7: This is some protocol I can't handle.\n"); ++ return info->invert; ++ } ++ ++ /* Treat the parent and all its children together as one connection, ++ except for the purpose of setting conntrack->layer7.app_proto in the ++ actual connection. This makes /proc/net/ip_conntrack somewhat more ++ satisfying. */ ++ if(!(conntrack = ip_conntrack_get((struct sk_buff *)skb, &ctinfo)) || ++ !(master_conntrack = ip_conntrack_get((struct sk_buff *)skb, &master_ctinfo))) { ++ DPRINTK("layer7: packet is not from a known connection, giving up.\n"); ++ return info->invert; ++ } ++ ++ /* Try to get a master conntrack (and its master etc) for FTP, etc. */ ++ while (master_ct(master_conntrack) != NULL) ++ master_conntrack = master_ct(master_conntrack); ++ ++ if(!skb->cb[0]){ ++ WRITE_LOCK(&ct_lock); ++ master_conntrack->layer7.numpackets++;/*starts at 0 via memset*/ ++ WRITE_UNLOCK(&ct_lock); ++ } ++ ++ /* if we've classified it or seen too many packets */ ++ if(TOTAL_PACKETS > num_packets || ++ master_conntrack->layer7.app_proto) { ++ ++ pattern_result = match_no_append(conntrack, master_conntrack, ctinfo, master_ctinfo, info); ++ ++ /* skb->cb[0] == seen. Avoid doing things twice if there are two l7 ++ rules. I'm not sure that using cb for this purpose is correct, although ++ it says "put your private variables there". But it doesn't look like it ++ is being used for anything else in the skbs that make it here. How can ++ I write to cb without making the compiler angry? */ ++ skb->cb[0] = 1; /* marking it seen here is probably irrelevant, but consistant */ ++ ++ return (pattern_result ^ info->invert); ++ } ++ ++ if(skb_is_nonlinear(skb)){ ++ if(skb_linearize(skb, GFP_ATOMIC) != 0){ ++ if (net_ratelimit()) ++ printk(KERN_ERR "layer7: failed to linearize packet, bailing.\n"); ++ return info->invert; ++ } ++ } ++ ++ /* now that the skb is linearized, it's safe to set these. */ ++ app_data = skb->data + app_data_offset(skb); ++ appdatalen = skb->tail - app_data; ++ ++ LOCK_BH(&list_lock); ++ /* the return value gets checked later, when we're ready to use it */ ++ comppattern = compile_and_cache(info->pattern, info->protocol); ++ UNLOCK_BH(&list_lock); ++ ++ /* On the first packet of a connection, allocate space for app data */ ++ WRITE_LOCK(&ct_lock); ++ if(TOTAL_PACKETS == 1 && !skb->cb[0] && !master_conntrack->layer7.app_data) { ++ master_conntrack->layer7.app_data = kmalloc(CONFIG_IP_NF_MATCH_LAYER7_MAXDATALEN, GFP_ATOMIC); ++ if(!master_conntrack->layer7.app_data){ ++ if (net_ratelimit()) ++ printk(KERN_ERR "layer7: out of memory in match, bailing.\n"); ++ WRITE_UNLOCK(&ct_lock); ++ return info->invert; ++ } ++ ++ master_conntrack->layer7.app_data[0] = '\0'; ++ } ++ WRITE_UNLOCK(&ct_lock); ++ ++ /* Can be here, but unallocated, if numpackets is increased near ++ the beginning of a connection */ ++ if(master_conntrack->layer7.app_data == NULL) ++ return (info->invert); /* unmatched */ ++ ++ if(!skb->cb[0]){ ++ int newbytes; ++ WRITE_LOCK(&ct_lock); ++ newbytes = add_data(master_conntrack, app_data, appdatalen); ++ WRITE_UNLOCK(&ct_lock); ++ ++ if(newbytes == 0) { /* didn't add any data */ ++ skb->cb[0] = 1; ++ /* Didn't match before, not going to match now */ ++ return info->invert; ++ } ++ } ++ ++ /* If looking for "unknown", then never match. "Unknown" means that ++ we've given up; we're still trying with these packets. */ ++ if(!strcmp(info->protocol, "unknown")) { ++ pattern_result = 0; ++ /* If the regexp failed to compile, don't bother running it */ ++ } else if(comppattern && regexec(comppattern, master_conntrack->layer7.app_data)) { ++ DPRINTK("layer7: regexec positive: %s!\n", info->protocol); ++ pattern_result = 1; ++ } else pattern_result = 0; ++ ++ if(pattern_result) { ++ WRITE_LOCK(&ct_lock); ++ master_conntrack->layer7.app_proto = kmalloc(strlen(info->protocol)+1, GFP_ATOMIC); ++ if(!master_conntrack->layer7.app_proto){ ++ if (net_ratelimit()) ++ printk(KERN_ERR "layer7: out of memory in match, bailing.\n"); ++ WRITE_UNLOCK(&ct_lock); ++ return (pattern_result ^ info->invert); ++ } ++ strcpy(master_conntrack->layer7.app_proto, info->protocol); ++ WRITE_UNLOCK(&ct_lock); ++ } ++ ++ /* mark the packet seen */ ++ skb->cb[0] = 1; ++ ++ return (pattern_result ^ 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_layer7_info))) ++ return 0; ++ return 1; ++} ++ ++static struct ipt_match layer7_match = { ++ .name = "layer7", ++ .match = &match, ++ .checkentry = &checkentry, ++ .me = THIS_MODULE ++}; ++ ++/* taken from drivers/video/modedb.c */ ++static int my_atoi(const char *s) ++{ ++ int val = 0; ++ ++ for (;; s++) { ++ switch (*s) { ++ case '0'...'9': ++ val = 10*val+(*s-'0'); ++ break; ++ default: ++ return val; ++ } ++ } ++} ++ ++/* write out num_packets to userland. */ ++static int layer7_read_proc(char* page, char ** start, off_t off, int count, ++ int* eof, void * data) ++{ ++ if(num_packets > 99 && net_ratelimit()) ++ printk(KERN_ERR "layer7: NOT REACHED. num_packets too big\n"); ++ ++ page[0] = num_packets/10 + '0'; ++ page[1] = num_packets%10 + '0'; ++ page[2] = '\n'; ++ page[3] = '\0'; ++ ++ *eof=1; ++ ++ return 3; ++} ++ ++/* Read in num_packets from userland */ ++static int layer7_write_proc(struct file* file, const char* buffer, ++ unsigned long count, void *data) ++{ ++ char * foo = kmalloc(count, GFP_ATOMIC); ++ ++ if(!foo){ ++ if (net_ratelimit()) ++ printk(KERN_ERR "layer7: out of memory, bailing. num_packets unchanged.\n"); ++ return count; ++ } ++ ++ copy_from_user(foo, buffer, count); ++ ++ num_packets = my_atoi(foo); ++ kfree (foo); ++ ++ /* This has an arbitrary limit to make the math easier. I'm lazy. ++ But anyway, 99 is a LOT! If you want more, you're doing it wrong! */ ++ if(num_packets > 99) { ++ printk(KERN_WARNING "layer7: num_packets can't be > 99.\n"); ++ num_packets = 99; ++ } else if(num_packets < 1) { ++ printk(KERN_WARNING "layer7: num_packets can't be < 1.\n"); ++ num_packets = 1; ++ } ++ ++ return count; ++} ++ ++/* register the proc file */ ++static void layer7_init_proc(void) ++{ ++ struct proc_dir_entry* entry; ++ entry = create_proc_entry("layer7_numpackets", 0644, proc_net); ++ entry->read_proc = layer7_read_proc; ++ entry->write_proc = layer7_write_proc; ++} ++ ++static void layer7_cleanup_proc(void) ++{ ++ remove_proc_entry("layer7_numpackets", proc_net); ++} ++ ++static int __init init(void) ++{ ++ layer7_init_proc(); ++ return ipt_register_match(&layer7_match); ++} ++ ++static void __exit fini(void) ++{ ++ layer7_cleanup_proc(); ++ ipt_unregister_match(&layer7_match); ++} ++ ++module_init(init); ++module_exit(fini); +diff -Nurp linux-2.4.30/net/ipv4/netfilter/regexp/regexp.c linux-2.4.30-layer7/net/ipv4/netfilter/regexp/regexp.c +--- linux-2.4.30/net/ipv4/netfilter/regexp/regexp.c 1969-12-31 18:00:00.000000000 -0600 ++++ linux-2.4.30-layer7/net/ipv4/netfilter/regexp/regexp.c 2005-05-03 18:37:03.000000000 -0500 +@@ -0,0 +1,1195 @@ ++/* ++ * regcomp and regexec -- regsub and regerror are elsewhere ++ * @(#)regexp.c 1.3 of 18 April 87 ++ * ++ * Copyright (c) 1986 by University of Toronto. ++ * Written by Henry Spencer. Not derived from licensed software. ++ * ++ * Permission is granted to anyone to use this software for any ++ * purpose on any computer system, and to redistribute it freely, ++ * subject to the following restrictions: ++ * ++ * 1. The author is not responsible for the consequences of use of ++ * this software, no matter how awful, even if they arise ++ * from defects in it. ++ * ++ * 2. The origin of this software must not be misrepresented, either ++ * by explicit claim or by omission. ++ * ++ * 3. Altered versions must be plainly marked as such, and must not ++ * be misrepresented as being the original software. ++ * ++ * Beware that some of this code is subtly aware of the way operator ++ * precedence is structured in regular expressions. Serious changes in ++ * regular-expression syntax might require a total rethink. ++ * ++ * This code was modified by Ethan Sommer to work within the kernel ++ * (it now uses kmalloc etc..) ++ * ++ * Modified slightly by Matthew Strait to use more modern C. ++ */ ++ ++#include "regexp.h" ++#include "regmagic.h" ++ ++/* added by ethan and matt. Lets it work in both kernel and user space. ++(So iptables can use it, for instance.) Yea, it goes both ways... */ ++#if __KERNEL__ ++ #define malloc(foo) kmalloc(foo,GFP_ATOMIC) ++#else ++ #define printk(format,args...) printf(format,##args) ++#endif ++ ++void regerror(char * s) ++{ ++ printk("<3>Regexp: %s\n", s); ++ /* NOTREACHED */ ++} ++ ++/* ++ * The "internal use only" fields in regexp.h are present to pass info from ++ * compile to execute that permits the execute phase to run lots faster on ++ * simple cases. They are: ++ * ++ * regstart char that must begin a match; '\0' if none obvious ++ * reganch is the match anchored (at beginning-of-line only)? ++ * regmust string (pointer into program) that match must include, or NULL ++ * regmlen length of regmust string ++ * ++ * Regstart and reganch permit very fast decisions on suitable starting points ++ * for a match, cutting down the work a lot. Regmust permits fast rejection ++ * of lines that cannot possibly match. The regmust tests are costly enough ++ * that regcomp() supplies a regmust only if the r.e. contains something ++ * potentially expensive (at present, the only such thing detected is * or + ++ * at the start of the r.e., which can involve a lot of backup). Regmlen is ++ * supplied because the test in regexec() needs it and regcomp() is computing ++ * it anyway. ++ */ ++ ++/* ++ * Structure for regexp "program". This is essentially a linear encoding ++ * of a nondeterministic finite-state machine (aka syntax charts or ++ * "railroad normal form" in parsing technology). Each node is an opcode ++ * plus a "next" pointer, possibly plus an operand. "Next" pointers of ++ * all nodes except BRANCH implement concatenation; a "next" pointer with ++ * a BRANCH on both ends of it is connecting two alternatives. (Here we ++ * have one of the subtle syntax dependencies: an individual BRANCH (as ++ * opposed to a collection of them) is never concatenated with anything ++ * because of operator precedence.) The operand of some types of node is ++ * a literal string; for others, it is a node leading into a sub-FSM. In ++ * particular, the operand of a BRANCH node is the first node of the branch. ++ * (NB this is *not* a tree structure: the tail of the branch connects ++ * to the thing following the set of BRANCHes.) The opcodes are: ++ */ ++ ++/* definition number opnd? meaning */ ++#define END 0 /* no End of program. */ ++#define BOL 1 /* no Match "" at beginning of line. */ ++#define EOL 2 /* no Match "" at end of line. */ ++#define ANY 3 /* no Match any one character. */ ++#define ANYOF 4 /* str Match any character in this string. */ ++#define ANYBUT 5 /* str Match any character not in this string. */ ++#define BRANCH 6 /* node Match this alternative, or the next... */ ++#define BACK 7 /* no Match "", "next" ptr points backward. */ ++#define EXACTLY 8 /* str Match this string. */ ++#define NOTHING 9 /* no Match empty string. */ ++#define STAR 10 /* node Match this (simple) thing 0 or more times. */ ++#define PLUS 11 /* node Match this (simple) thing 1 or more times. */ ++#define OPEN 20 /* no Mark this point in input as start of #n. */ ++ /* OPEN+1 is number 1, etc. */ ++#define CLOSE 30 /* no Analogous to OPEN. */ ++ ++/* ++ * Opcode notes: ++ * ++ * BRANCH The set of branches constituting a single choice are hooked ++ * together with their "next" pointers, since precedence prevents ++ * anything being concatenated to any individual branch. The ++ * "next" pointer of the last BRANCH in a choice points to the ++ * thing following the whole choice. This is also where the ++ * final "next" pointer of each individual branch points; each ++ * branch starts with the operand node of a BRANCH node. ++ * ++ * BACK Normal "next" pointers all implicitly point forward; BACK ++ * exists to make loop structures possible. ++ * ++ * STAR,PLUS '?', and complex '*' and '+', are implemented as circular ++ * BRANCH structures using BACK. Simple cases (one character ++ * per match) are implemented with STAR and PLUS for speed ++ * and to minimize recursive plunges. ++ * ++ * OPEN,CLOSE ...are numbered at compile time. ++ */ ++ ++/* ++ * A node is one char of opcode followed by two chars of "next" pointer. ++ * "Next" pointers are stored as two 8-bit pieces, high order first. The ++ * value is a positive offset from the opcode of the node containing it. ++ * An operand, if any, simply follows the node. (Note that much of the ++ * code generation knows about this implicit relationship.) ++ * ++ * Using two bytes for the "next" pointer is vast overkill for most things, ++ * but allows patterns to get big without disasters. ++ */ ++#define OP(p) (*(p)) ++#define NEXT(p) (((*((p)+1)&0377)<<8) + (*((p)+2)&0377)) ++#define OPERAND(p) ((p) + 3) ++ ++/* ++ * See regmagic.h for one further detail of program structure. ++ */ ++ ++ ++/* ++ * Utility definitions. ++ */ ++#ifndef CHARBITS ++#define UCHARAT(p) ((int)*(unsigned char *)(p)) ++#else ++#define UCHARAT(p) ((int)*(p)&CHARBITS) ++#endif ++ ++#define FAIL(m) { regerror(m); return(NULL); } ++#define ISMULT(c) ((c) == '*' || (c) == '+' || (c) == '?') ++#define META "^$.[()|?+*\\" ++ ++/* ++ * Flags to be passed up and down. ++ */ ++#define HASWIDTH 01 /* Known never to match null string. */ ++#define SIMPLE 02 /* Simple enough to be STAR/PLUS operand. */ ++#define SPSTART 04 /* Starts with * or +. */ ++#define WORST 0 /* Worst case. */ ++ ++/* ++ * Global work variables for regcomp(). ++ */ ++static char *regparse; /* Input-scan pointer. */ ++static int regnpar; /* () count. */ ++static char regdummy; ++static char *regcode; /* Code-emit pointer; ®dummy = don't. */ ++static long regsize; /* Code size. */ ++ ++/* ++ * Forward declarations for regcomp()'s friends. ++ */ ++#ifndef STATIC ++#define STATIC static ++#endif ++STATIC char *reg(int paren,int *flagp); ++STATIC char *regbranch(int *flagp); ++STATIC char *regpiece(int *flagp); ++STATIC char *regatom(int *flagp); ++STATIC char *regnode(char op); ++STATIC char *regnext(char *p); ++STATIC void regc(char b); ++STATIC void reginsert(char op, char *opnd); ++STATIC void regtail(char *p, char *val); ++STATIC void regoptail(char *p, char *val); ++ ++ ++__kernel_size_t my_strcspn(const char *s1,const char *s2) ++{ ++ char *scan1; ++ char *scan2; ++ int count; ++ ++ count = 0; ++ for (scan1 = (char *)s1; *scan1 != '\0'; scan1++) { ++ for (scan2 = (char *)s2; *scan2 != '\0';) /* ++ moved down. */ ++ if (*scan1 == *scan2++) ++ return(count); ++ count++; ++ } ++ return(count); ++} ++ ++/* ++ - regcomp - compile a regular expression into internal code ++ * ++ * We can't allocate space until we know how big the compiled form will be, ++ * but we can't compile it (and thus know how big it is) until we've got a ++ * place to put the code. So we cheat: we compile it twice, once with code ++ * generation turned off and size counting turned on, and once "for real". ++ * This also means that we don't allocate space until we are sure that the ++ * thing really will compile successfully, and we never have to move the ++ * code and thus invalidate pointers into it. (Note that it has to be in ++ * one piece because free() must be able to free it all.) ++ * ++ * Beware that the optimization-preparation code in here knows about some ++ * of the structure of the compiled regexp. ++ */ ++regexp * ++regcomp(char *exp,int *patternsize) ++{ ++ register regexp *r; ++ register char *scan; ++ register char *longest; ++ register int len; ++ int flags; ++ /* commented out by ethan ++ extern char *malloc(); ++ */ ++ ++ if (exp == NULL) ++ FAIL("NULL argument"); ++ ++ /* First pass: determine size, legality. */ ++ regparse = exp; ++ regnpar = 1; ++ regsize = 0L; ++ regcode = ®dummy; ++ regc(MAGIC); ++ if (reg(0, &flags) == NULL) ++ return(NULL); ++ ++ /* Small enough for pointer-storage convention? */ ++ if (regsize >= 32767L) /* Probably could be 65535L. */ ++ FAIL("regexp too big"); ++ ++ /* Allocate space. */ ++ *patternsize=sizeof(regexp) + (unsigned)regsize; ++ r = (regexp *)malloc(sizeof(regexp) + (unsigned)regsize); ++ if (r == NULL) ++ FAIL("out of space"); ++ ++ /* Second pass: emit code. */ ++ regparse = exp; ++ regnpar = 1; ++ regcode = r->program; ++ regc(MAGIC); ++ if (reg(0, &flags) == NULL) ++ return(NULL); ++ ++ /* Dig out information for optimizations. */ ++ r->regstart = '\0'; /* Worst-case defaults. */ ++ r->reganch = 0; ++ r->regmust = NULL; ++ r->regmlen = 0; ++ scan = r->program+1; /* First BRANCH. */ ++ if (OP(regnext(scan)) == END) { /* Only one top-level choice. */ ++ scan = OPERAND(scan); ++ ++ /* Starting-point info. */ ++ if (OP(scan) == EXACTLY) ++ r->regstart = *OPERAND(scan); ++ else if (OP(scan) == BOL) ++ r->reganch++; ++ ++ /* ++ * If there's something expensive in the r.e., find the ++ * longest literal string that must appear and make it the ++ * regmust. Resolve ties in favor of later strings, since ++ * the regstart check works with the beginning of the r.e. ++ * and avoiding duplication strengthens checking. Not a ++ * strong reason, but sufficient in the absence of others. ++ */ ++ if (flags&SPSTART) { ++ longest = NULL; ++ len = 0; ++ for (; scan != NULL; scan = regnext(scan)) ++ if (OP(scan) == EXACTLY && strlen(OPERAND(scan)) >= len) { ++ longest = OPERAND(scan); ++ len = strlen(OPERAND(scan)); ++ } ++ r->regmust = longest; ++ r->regmlen = len; ++ } ++ } ++ ++ return(r); ++} ++ ++/* ++ - reg - regular expression, i.e. main body or parenthesized thing ++ * ++ * Caller must absorb opening parenthesis. ++ * ++ * Combining parenthesis handling with the base level of regular expression ++ * is a trifle forced, but the need to tie the tails of the branches to what ++ * follows makes it hard to avoid. ++ */ ++static char * ++reg(int paren, int *flagp /* Parenthesized? */ ) ++{ ++ register char *ret; ++ register char *br; ++ register char *ender; ++ register int parno = 0; /* 0 makes gcc happy */ ++ int flags; ++ ++ *flagp = HASWIDTH; /* Tentatively. */ ++ ++ /* Make an OPEN node, if parenthesized. */ ++ if (paren) { ++ if (regnpar >= NSUBEXP) ++ FAIL("too many ()"); ++ parno = regnpar; ++ regnpar++; ++ ret = regnode(OPEN+parno); ++ } else ++ ret = NULL; ++ ++ /* Pick up the branches, linking them together. */ ++ br = regbranch(&flags); ++ if (br == NULL) ++ return(NULL); ++ if (ret != NULL) ++ regtail(ret, br); /* OPEN -> first. */ ++ else ++ ret = br; ++ if (!(flags&HASWIDTH)) ++ *flagp &= ~HASWIDTH; ++ *flagp |= flags&SPSTART; ++ while (*regparse == '|') { ++ regparse++; ++ br = regbranch(&flags); ++ if (br == NULL) ++ return(NULL); ++ regtail(ret, br); /* BRANCH -> BRANCH. */ ++ if (!(flags&HASWIDTH)) ++ *flagp &= ~HASWIDTH; ++ *flagp |= flags&SPSTART; ++ } ++ ++ /* Make a closing node, and hook it on the end. */ ++ ender = regnode((paren) ? CLOSE+parno : END); ++ regtail(ret, ender); ++ ++ /* Hook the tails of the branches to the closing node. */ ++ for (br = ret; br != NULL; br = regnext(br)) ++ regoptail(br, ender); ++ ++ /* Check for proper termination. */ ++ if (paren && *regparse++ != ')') { ++ FAIL("unmatched ()"); ++ } else if (!paren && *regparse != '\0') { ++ if (*regparse == ')') { ++ FAIL("unmatched ()"); ++ } else ++ FAIL("junk on end"); /* "Can't happen". */ ++ /* NOTREACHED */ ++ } ++ ++ return(ret); ++} ++ ++/* ++ - regbranch - one alternative of an | operator ++ * ++ * Implements the concatenation operator. ++ */ ++static char * ++regbranch(int *flagp) ++{ ++ register char *ret; ++ register char *chain; ++ register char *latest; ++ int flags; ++ ++ *flagp = WORST; /* Tentatively. */ ++ ++ ret = regnode(BRANCH); ++ chain = NULL; ++ while (*regparse != '\0' && *regparse != '|' && *regparse != ')') { ++ latest = regpiece(&flags); ++ if (latest == NULL) ++ return(NULL); ++ *flagp |= flags&HASWIDTH; ++ if (chain == NULL) /* First piece. */ ++ *flagp |= flags&SPSTART; ++ else ++ regtail(chain, latest); ++ chain = latest; ++ } ++ if (chain == NULL) /* Loop ran zero times. */ ++ (void) regnode(NOTHING); ++ ++ return(ret); ++} ++ ++/* ++ - regpiece - something followed by possible [*+?] ++ * ++ * Note that the branching code sequences used for ? and the general cases ++ * of * and + are somewhat optimized: they use the same NOTHING node as ++ * both the endmarker for their branch list and the body of the last branch. ++ * It might seem that this node could be dispensed with entirely, but the ++ * endmarker role is not redundant. ++ */ ++static char * ++regpiece(int *flagp) ++{ ++ register char *ret; ++ register char op; ++ register char *next; ++ int flags; ++ ++ ret = regatom(&flags); ++ if (ret == NULL) ++ return(NULL); ++ ++ op = *regparse; ++ if (!ISMULT(op)) { ++ *flagp = flags; ++ return(ret); ++ } ++ ++ if (!(flags&HASWIDTH) && op != '?') ++ FAIL("*+ operand could be empty"); ++ *flagp = (op != '+') ? (WORST|SPSTART) : (WORST|HASWIDTH); ++ ++ if (op == '*' && (flags&SIMPLE)) ++ reginsert(STAR, ret); ++ else if (op == '*') { ++ /* Emit x* as (x&|), where & means "self". */ ++ reginsert(BRANCH, ret); /* Either x */ ++ regoptail(ret, regnode(BACK)); /* and loop */ ++ regoptail(ret, ret); /* back */ ++ regtail(ret, regnode(BRANCH)); /* or */ ++ regtail(ret, regnode(NOTHING)); /* null. */ ++ } else if (op == '+' && (flags&SIMPLE)) ++ reginsert(PLUS, ret); ++ else if (op == '+') { ++ /* Emit x+ as x(&|), where & means "self". */ ++ next = regnode(BRANCH); /* Either */ ++ regtail(ret, next); ++ regtail(regnode(BACK), ret); /* loop back */ ++ regtail(next, regnode(BRANCH)); /* or */ ++ regtail(ret, regnode(NOTHING)); /* null. */ ++ } else if (op == '?') { ++ /* Emit x? as (x|) */ ++ reginsert(BRANCH, ret); /* Either x */ ++ regtail(ret, regnode(BRANCH)); /* or */ ++ next = regnode(NOTHING); /* null. */ ++ regtail(ret, next); ++ regoptail(ret, next); ++ } ++ regparse++; ++ if (ISMULT(*regparse)) ++ FAIL("nested *?+"); ++ ++ return(ret); ++} ++ ++/* ++ - regatom - the lowest level ++ * ++ * Optimization: gobbles an entire sequence of ordinary characters so that ++ * it can turn them into a single node, which is smaller to store and ++ * faster to run. Backslashed characters are exceptions, each becoming a ++ * separate node; the code is simpler that way and it's not worth fixing. ++ */ ++static char * ++regatom(int *flagp) ++{ ++ register char *ret; ++ int flags; ++ ++ *flagp = WORST; /* Tentatively. */ ++ ++ switch (*regparse++) { ++ case '^': ++ ret = regnode(BOL); ++ break; ++ case '$': ++ ret = regnode(EOL); ++ break; ++ case '.': ++ ret = regnode(ANY); ++ *flagp |= HASWIDTH|SIMPLE; ++ break; ++ case '[': { ++ register int class; ++ register int classend; ++ ++ if (*regparse == '^') { /* Complement of range. */ ++ ret = regnode(ANYBUT); ++ regparse++; ++ } else ++ ret = regnode(ANYOF); ++ if (*regparse == ']' || *regparse == '-') ++ regc(*regparse++); ++ while (*regparse != '\0' && *regparse != ']') { ++ if (*regparse == '-') { ++ regparse++; ++ if (*regparse == ']' || *regparse == '\0') ++ regc('-'); ++ else { ++ class = UCHARAT(regparse-2)+1; ++ classend = UCHARAT(regparse); ++ if (class > classend+1) ++ FAIL("invalid [] range"); ++ for (; class <= classend; class++) ++ regc(class); ++ regparse++; ++ } ++ } else ++ regc(*regparse++); ++ } ++ regc('\0'); ++ if (*regparse != ']') ++ FAIL("unmatched []"); ++ regparse++; ++ *flagp |= HASWIDTH|SIMPLE; ++ } ++ break; ++ case '(': ++ ret = reg(1, &flags); ++ if (ret == NULL) ++ return(NULL); ++ *flagp |= flags&(HASWIDTH|SPSTART); ++ break; ++ case '\0': ++ case '|': ++ case ')': ++ FAIL("internal urp"); /* Supposed to be caught earlier. */ ++ break; ++ case '?': ++ case '+': ++ case '*': ++ FAIL("?+* follows nothing"); ++ break; ++ case '\\': ++ if (*regparse == '\0') ++ FAIL("trailing \\"); ++ ret = regnode(EXACTLY); ++ regc(*regparse++); ++ regc('\0'); ++ *flagp |= HASWIDTH|SIMPLE; ++ break; ++ default: { ++ register int len; ++ register char ender; ++ ++ regparse--; ++ len = my_strcspn((const char *)regparse, (const char *)META); ++ if (len <= 0) ++ FAIL("internal disaster"); ++ ender = *(regparse+len); ++ if (len > 1 && ISMULT(ender)) ++ len--; /* Back off clear of ?+* operand. */ ++ *flagp |= HASWIDTH; ++ if (len == 1) ++ *flagp |= SIMPLE; ++ ret = regnode(EXACTLY); ++ while (len > 0) { ++ regc(*regparse++); ++ len--; ++ } ++ regc('\0'); ++ } ++ break; ++ } ++ ++ return(ret); ++} ++ ++/* ++ - regnode - emit a node ++ */ ++static char * /* Location. */ ++regnode(char op) ++{ ++ register char *ret; ++ register char *ptr; ++ ++ ret = regcode; ++ if (ret == ®dummy) { ++ regsize += 3; ++ return(ret); ++ } ++ ++ ptr = ret; ++ *ptr++ = op; ++ *ptr++ = '\0'; /* Null "next" pointer. */ ++ *ptr++ = '\0'; ++ regcode = ptr; ++ ++ return(ret); ++} ++ ++/* ++ - regc - emit (if appropriate) a byte of code ++ */ ++static void ++regc(char b) ++{ ++ if (regcode != ®dummy) ++ *regcode++ = b; ++ else ++ regsize++; ++} ++ ++/* ++ - reginsert - insert an operator in front of already-emitted operand ++ * ++ * Means relocating the operand. ++ */ ++static void ++reginsert(char op, char* opnd) ++{ ++ register char *src; ++ register char *dst; ++ register char *place; ++ ++ if (regcode == ®dummy) { ++ regsize += 3; ++ return; ++ } ++ ++ src = regcode; ++ regcode += 3; ++ dst = regcode; ++ while (src > opnd) ++ *--dst = *--src; ++ ++ place = opnd; /* Op node, where operand used to be. */ ++ *place++ = op; ++ *place++ = '\0'; ++ *place++ = '\0'; ++} ++ ++/* ++ - regtail - set the next-pointer at the end of a node chain ++ */ ++static void ++regtail(char *p, char *val) ++{ ++ register char *scan; ++ register char *temp; ++ register int offset; ++ ++ if (p == ®dummy) ++ return; ++ ++ /* Find last node. */ ++ scan = p; ++ for (;;) { ++ temp = regnext(scan); ++ if (temp == NULL) ++ break; ++ scan = temp; ++ } ++ ++ if (OP(scan) == BACK) ++ offset = scan - val; ++ else ++ offset = val - scan; ++ *(scan+1) = (offset>>8)&0377; ++ *(scan+2) = offset&0377; ++} ++ ++/* ++ - regoptail - regtail on operand of first argument; nop if operandless ++ */ ++static void ++regoptail(char *p, char *val) ++{ ++ /* "Operandless" and "op != BRANCH" are synonymous in practice. */ ++ if (p == NULL || p == ®dummy || OP(p) != BRANCH) ++ return; ++ regtail(OPERAND(p), val); ++} ++ ++/* ++ * regexec and friends ++ */ ++ ++/* ++ * Global work variables for regexec(). ++ */ ++static char *reginput; /* String-input pointer. */ ++static char *regbol; /* Beginning of input, for ^ check. */ ++static char **regstartp; /* Pointer to startp array. */ ++static char **regendp; /* Ditto for endp. */ ++ ++/* ++ * Forwards. ++ */ ++STATIC int regtry(regexp *prog, char *string); ++STATIC int regmatch(char *prog); ++STATIC int regrepeat(char *p); ++ ++#ifdef DEBUG ++int regnarrate = 0; ++void regdump(); ++STATIC char *regprop(char *op); ++#endif ++ ++/* ++ - regexec - match a regexp against a string ++ */ ++int ++regexec(regexp *prog, char *string) ++{ ++ register char *s; ++ ++ /* Be paranoid... */ ++ if (prog == NULL || string == NULL) { ++ printk("<3>Regexp: NULL parameter\n"); ++ return(0); ++ } ++ ++ /* Check validity of program. */ ++ if (UCHARAT(prog->program) != MAGIC) { ++ printk("<3>Regexp: corrupted program\n"); ++ return(0); ++ } ++ ++ /* If there is a "must appear" string, look for it. */ ++ if (prog->regmust != NULL) { ++ s = string; ++ while ((s = strchr(s, prog->regmust[0])) != NULL) { ++ if (strncmp(s, prog->regmust, prog->regmlen) == 0) ++ break; /* Found it. */ ++ s++; ++ } ++ if (s == NULL) /* Not present. */ ++ return(0); ++ } ++ ++ /* Mark beginning of line for ^ . */ ++ regbol = string; ++ ++ /* Simplest case: anchored match need be tried only once. */ ++ if (prog->reganch) ++ return(regtry(prog, string)); ++ ++ /* Messy cases: unanchored match. */ ++ s = string; ++ if (prog->regstart != '\0') ++ /* We know what char it must start with. */ ++ while ((s = strchr(s, prog->regstart)) != NULL) { ++ if (regtry(prog, s)) ++ return(1); ++ s++; ++ } ++ else ++ /* We don't -- general case. */ ++ do { ++ if (regtry(prog, s)) ++ return(1); ++ } while (*s++ != '\0'); ++ ++ /* Failure. */ ++ return(0); ++} ++ ++/* ++ - regtry - try match at specific point ++ */ ++static int /* 0 failure, 1 success */ ++regtry(regexp *prog, char *string) ++{ ++ register int i; ++ register char **sp; ++ register char **ep; ++ ++ reginput = string; ++ regstartp = prog->startp; ++ regendp = prog->endp; ++ ++ sp = prog->startp; ++ ep = prog->endp; ++ for (i = NSUBEXP; i > 0; i--) { ++ *sp++ = NULL; ++ *ep++ = NULL; ++ } ++ if (regmatch(prog->program + 1)) { ++ prog->startp[0] = string; ++ prog->endp[0] = reginput; ++ return(1); ++ } else ++ return(0); ++} ++ ++/* ++ - regmatch - main matching routine ++ * ++ * Conceptually the strategy is simple: check to see whether the current ++ * node matches, call self recursively to see whether the rest matches, ++ * and then act accordingly. In practice we make some effort to avoid ++ * recursion, in particular by going through "ordinary" nodes (that don't ++ * need to know whether the rest of the match failed) by a loop instead of ++ * by recursion. ++ */ ++static int /* 0 failure, 1 success */ ++regmatch(char *prog) ++{ ++ register char *scan = prog; /* Current node. */ ++ char *next; /* Next node. */ ++ ++#ifdef DEBUG ++ if (scan != NULL && regnarrate) ++ fprintf(stderr, "%s(\n", regprop(scan)); ++#endif ++ while (scan != NULL) { ++#ifdef DEBUG ++ if (regnarrate) ++ fprintf(stderr, "%s...\n", regprop(scan)); ++#endif ++ next = regnext(scan); ++ ++ switch (OP(scan)) { ++ case BOL: ++ if (reginput != regbol) ++ return(0); ++ break; ++ case EOL: ++ if (*reginput != '\0') ++ return(0); ++ break; ++ case ANY: ++ if (*reginput == '\0') ++ return(0); ++ reginput++; ++ break; ++ case EXACTLY: { ++ register int len; ++ register char *opnd; ++ ++ opnd = OPERAND(scan); ++ /* Inline the first character, for speed. */ ++ if (*opnd != *reginput) ++ return(0); ++ len = strlen(opnd); ++ if (len > 1 && strncmp(opnd, reginput, len) != 0) ++ return(0); ++ reginput += len; ++ } ++ break; ++ case ANYOF: ++ if (*reginput == '\0' || strchr(OPERAND(scan), *reginput) == NULL) ++ return(0); ++ reginput++; ++ break; ++ case ANYBUT: ++ if (*reginput == '\0' || strchr(OPERAND(scan), *reginput) != NULL) ++ return(0); ++ reginput++; ++ break; ++ case NOTHING: ++ case BACK: ++ break; ++ case OPEN+1: ++ case OPEN+2: ++ case OPEN+3: ++ case OPEN+4: ++ case OPEN+5: ++ case OPEN+6: ++ case OPEN+7: ++ case OPEN+8: ++ case OPEN+9: { ++ register int no; ++ register char *save; ++ ++ no = OP(scan) - OPEN; ++ save = reginput; ++ ++ if (regmatch(next)) { ++ /* ++ * Don't set startp if some later ++ * invocation of the same parentheses ++ * already has. ++ */ ++ if (regstartp[no] == NULL) ++ regstartp[no] = save; ++ return(1); ++ } else ++ return(0); ++ } ++ break; ++ case CLOSE+1: ++ case CLOSE+2: ++ case CLOSE+3: ++ case CLOSE+4: ++ case CLOSE+5: ++ case CLOSE+6: ++ case CLOSE+7: ++ case CLOSE+8: ++ case CLOSE+9: ++ { ++ register int no; ++ register char *save; ++ ++ no = OP(scan) - CLOSE; ++ save = reginput; ++ ++ if (regmatch(next)) { ++ /* ++ * Don't set endp if some later ++ * invocation of the same parentheses ++ * already has. ++ */ ++ if (regendp[no] == NULL) ++ regendp[no] = save; ++ return(1); ++ } else ++ return(0); ++ } ++ break; ++ case BRANCH: { ++ register char *save; ++ ++ if (OP(next) != BRANCH) /* No choice. */ ++ next = OPERAND(scan); /* Avoid recursion. */ ++ else { ++ do { ++ save = reginput; ++ if (regmatch(OPERAND(scan))) ++ return(1); ++ reginput = save; ++ scan = regnext(scan); ++ } while (scan != NULL && OP(scan) == BRANCH); ++ return(0); ++ /* NOTREACHED */ ++ } ++ } ++ break; ++ case STAR: ++ case PLUS: { ++ register char nextch; ++ register int no; ++ register char *save; ++ register int min; ++ ++ /* ++ * Lookahead to avoid useless match attempts ++ * when we know what character comes next. ++ */ ++ nextch = '\0'; ++ if (OP(next) == EXACTLY) ++ nextch = *OPERAND(next); ++ min = (OP(scan) == STAR) ? 0 : 1; ++ save = reginput; ++ no = regrepeat(OPERAND(scan)); ++ while (no >= min) { ++ /* If it could work, try it. */ ++ if (nextch == '\0' || *reginput == nextch) ++ if (regmatch(next)) ++ return(1); ++ /* Couldn't or didn't -- back up. */ ++ no--; ++ reginput = save + no; ++ } ++ return(0); ++ } ++ break; ++ case END: ++ return(1); /* Success! */ ++ break; ++ default: ++ printk("<3>Regexp: memory corruption\n"); ++ return(0); ++ break; ++ } ++ ++ scan = next; ++ } ++ ++ /* ++ * We get here only if there's trouble -- normally "case END" is ++ * the terminating point. ++ */ ++ printk("<3>Regexp: corrupted pointers\n"); ++ return(0); ++} ++ ++/* ++ - regrepeat - repeatedly match something simple, report how many ++ */ ++static int ++regrepeat(char *p) ++{ ++ register int count = 0; ++ register char *scan; ++ register char *opnd; ++ ++ scan = reginput; ++ opnd = OPERAND(p); ++ switch (OP(p)) { ++ case ANY: ++ count = strlen(scan); ++ scan += count; ++ break; ++ case EXACTLY: ++ while (*opnd == *scan) { ++ count++; ++ scan++; ++ } ++ break; ++ case ANYOF: ++ while (*scan != '\0' && strchr(opnd, *scan) != NULL) { ++ count++; ++ scan++; ++ } ++ break; ++ case ANYBUT: ++ while (*scan != '\0' && strchr(opnd, *scan) == NULL) { ++ count++; ++ scan++; ++ } ++ break; ++ default: /* Oh dear. Called inappropriately. */ ++ printk("<3>Regexp: internal foulup\n"); ++ count = 0; /* Best compromise. */ ++ break; ++ } ++ reginput = scan; ++ ++ return(count); ++} ++ ++/* ++ - regnext - dig the "next" pointer out of a node ++ */ ++static char* ++regnext(char *p) ++{ ++ register int offset; ++ ++ if (p == ®dummy) ++ return(NULL); ++ ++ offset = NEXT(p); ++ if (offset == 0) ++ return(NULL); ++ ++ if (OP(p) == BACK) ++ return(p-offset); ++ else ++ return(p+offset); ++} ++ ++#ifdef DEBUG ++ ++STATIC char *regprop(); ++ ++/* ++ - regdump - dump a regexp onto stdout in vaguely comprehensible form ++ */ ++void ++regdump(regexp *r) ++{ ++ register char *s; ++ register char op = EXACTLY; /* Arbitrary non-END op. */ ++ register char *next; ++ /* extern char *strchr(); */ ++ ++ ++ s = r->program + 1; ++ while (op != END) { /* While that wasn't END last time... */ ++ op = OP(s); ++ printf("%2d%s", s-r->program, regprop(s)); /* Where, what. */ ++ next = regnext(s); ++ if (next == NULL) /* Next ptr. */ ++ printf("(0)"); ++ else ++ printf("(%d)", (s-r->program)+(next-s)); ++ s += 3; ++ if (op == ANYOF || op == ANYBUT || op == EXACTLY) { ++ /* Literal string, where present. */ ++ while (*s != '\0') { ++ putchar(*s); ++ s++; ++ } ++ s++; ++ } ++ putchar('\n'); ++ } ++ ++ /* Header fields of interest. */ ++ if (r->regstart != '\0') ++ printf("start `%c' ", r->regstart); ++ if (r->reganch) ++ printf("anchored "); ++ if (r->regmust != NULL) ++ printf("must have \"%s\"", r->regmust); ++ printf("\n"); ++} ++ ++/* ++ - regprop - printable representation of opcode ++ */ ++static char * ++regprop(char *op) ++{ ++#define BUFLEN 50 ++ register char *p; ++ static char buf[BUFLEN]; ++ ++ strcpy(buf, ":"); ++ ++ switch (OP(op)) { ++ case BOL: ++ p = "BOL"; ++ break; ++ case EOL: ++ p = "EOL"; ++ break; ++ case ANY: ++ p = "ANY"; ++ break; ++ case ANYOF: ++ p = "ANYOF"; ++ break; ++ case ANYBUT: ++ p = "ANYBUT"; ++ break; ++ case BRANCH: ++ p = "BRANCH"; ++ break; ++ case EXACTLY: ++ p = "EXACTLY"; ++ break; ++ case NOTHING: ++ p = "NOTHING"; ++ break; ++ case BACK: ++ p = "BACK"; ++ break; ++ case END: ++ p = "END"; ++ break; ++ case OPEN+1: ++ case OPEN+2: ++ case OPEN+3: ++ case OPEN+4: ++ case OPEN+5: ++ case OPEN+6: ++ case OPEN+7: ++ case OPEN+8: ++ case OPEN+9: ++ snprintf(buf+strlen(buf),BUFLEN-strlen(buf), "OPEN%d", OP(op)-OPEN); ++ p = NULL; ++ break; ++ case CLOSE+1: ++ case CLOSE+2: ++ case CLOSE+3: ++ case CLOSE+4: ++ case CLOSE+5: ++ case CLOSE+6: ++ case CLOSE+7: ++ case CLOSE+8: ++ case CLOSE+9: ++ snprintf(buf+strlen(buf),BUFLEN-strlen(buf), "CLOSE%d", OP(op)-CLOSE); ++ p = NULL; ++ break; ++ case STAR: ++ p = "STAR"; ++ break; ++ case PLUS: ++ p = "PLUS"; ++ break; ++ default: ++ printk("<3>Regexp: corrupted opcode\n"); ++ break; ++ } ++ if (p != NULL) ++ strncat(buf, p, BUFLEN-strlen(buf)); ++ return(buf); ++} ++#endif ++ ++ +diff -Nurp linux-2.4.30/net/ipv4/netfilter/regexp/regexp.h linux-2.4.30-layer7/net/ipv4/netfilter/regexp/regexp.h +--- linux-2.4.30/net/ipv4/netfilter/regexp/regexp.h 1969-12-31 18:00:00.000000000 -0600 ++++ linux-2.4.30-layer7/net/ipv4/netfilter/regexp/regexp.h 2005-05-03 18:37:03.000000000 -0500 +@@ -0,0 +1,40 @@ ++/* ++ * Definitions etc. for regexp(3) routines. ++ * ++ * Caveat: this is V8 regexp(3) [actually, a reimplementation thereof], ++ * not the System V one. ++ */ ++ ++#ifndef REGEXP_H ++#define REGEXP_H ++ ++/* ++http://www.opensource.apple.com/darwinsource/10.3/expect-1/expect/expect.h , ++which contains a version of this library, says: ++ ++ * ++ * NSUBEXP must be at least 10, and no greater than 117 or the parser ++ * will not work properly. ++ * ++ ++However, it looks rather like this library is limited to 10. If you think ++otherwise, let us know. ++*/ ++ ++#define NSUBEXP 10 ++typedef struct regexp { ++ char *startp[NSUBEXP]; ++ char *endp[NSUBEXP]; ++ char regstart; /* Internal use only. */ ++ char reganch; /* Internal use only. */ ++ char *regmust; /* Internal use only. */ ++ int regmlen; /* Internal use only. */ ++ char program[1]; /* Unwarranted chumminess with compiler. */ ++} regexp; ++ ++regexp * regcomp(char *exp, int *patternsize); ++int regexec(regexp *prog, char *string); ++void regsub(regexp *prog, char *source, char *dest); ++void regerror(char *s); ++ ++#endif +diff -Nurp linux-2.4.30/net/ipv4/netfilter/regexp/regmagic.h linux-2.4.30-layer7/net/ipv4/netfilter/regexp/regmagic.h +--- linux-2.4.30/net/ipv4/netfilter/regexp/regmagic.h 1969-12-31 18:00:00.000000000 -0600 ++++ linux-2.4.30-layer7/net/ipv4/netfilter/regexp/regmagic.h 2005-05-03 18:37:03.000000000 -0500 +@@ -0,0 +1,5 @@ ++/* ++ * The first byte of the regexp internal "program" is actually this magic ++ * number; the start node begins in the second byte. ++ */ ++#define MAGIC 0234 +diff -Nurp linux-2.4.30/net/ipv4/netfilter/regexp/regsub.c linux-2.4.30-layer7/net/ipv4/netfilter/regexp/regsub.c +--- linux-2.4.30/net/ipv4/netfilter/regexp/regsub.c 1969-12-31 18:00:00.000000000 -0600 ++++ linux-2.4.30-layer7/net/ipv4/netfilter/regexp/regsub.c 2005-05-03 18:37:03.000000000 -0500 +@@ -0,0 +1,95 @@ ++/* ++ * regsub ++ * @(#)regsub.c 1.3 of 2 April 86 ++ * ++ * Copyright (c) 1986 by University of Toronto. ++ * Written by Henry Spencer. Not derived from licensed software. ++ * ++ * Permission is granted to anyone to use this software for any ++ * purpose on any computer system, and to redistribute it freely, ++ * subject to the following restrictions: ++ * ++ * 1. The author is not responsible for the consequences of use of ++ * this software, no matter how awful, even if they arise ++ * from defects in it. ++ * ++ * 2. The origin of this software must not be misrepresented, either ++ * by explicit claim or by omission. ++ * ++ * 3. Altered versions must be plainly marked as such, and must not ++ * be misrepresented as being the original software. ++ * ++ * ++ * This code was modified by Ethan Sommer to work within the kernel ++ * (it now uses kmalloc etc..) ++ * ++ */ ++#include "regexp.h" ++#include "regmagic.h" ++#include <linux/string.h> ++ ++ ++#ifndef CHARBITS ++#define UCHARAT(p) ((int)*(unsigned char *)(p)) ++#else ++#define UCHARAT(p) ((int)*(p)&CHARBITS) ++#endif ++ ++#if 0 ++//void regerror(char * s) ++//{ ++// printk("regexp(3): %s", s); ++// /* NOTREACHED */ ++//} ++#endif ++ ++/* ++ - regsub - perform substitutions after a regexp match ++ */ ++void ++regsub(regexp * prog, char * source, char * dest) ++{ ++ register char *src; ++ register char *dst; ++ register char c; ++ register int no; ++ register int len; ++ ++ /* Not necessary and gcc doesn't like it -MLS */ ++ /*extern char *strncpy();*/ ++ ++ if (prog == NULL || source == NULL || dest == NULL) { ++ regerror("NULL parm to regsub"); ++ return; ++ } ++ if (UCHARAT(prog->program) != MAGIC) { ++ regerror("damaged regexp fed to regsub"); ++ return; ++ } ++ ++ src = source; ++ dst = dest; ++ while ((c = *src++) != '\0') { ++ if (c == '&') ++ no = 0; ++ else if (c == '\\' && '0' <= *src && *src <= '9') ++ no = *src++ - '0'; ++ else ++ no = -1; ++ ++ if (no < 0) { /* Ordinary character. */ ++ if (c == '\\' && (*src == '\\' || *src == '&')) ++ c = *src++; ++ *dst++ = c; ++ } else if (prog->startp[no] != NULL && prog->endp[no] != NULL) { ++ len = prog->endp[no] - prog->startp[no]; ++ (void) strncpy(dst, prog->startp[no], len); ++ dst += len; ++ if (len != 0 && *(dst-1) == '\0') { /* strncpy hit NUL. */ ++ regerror("damaged match string"); ++ return; ++ } ++ } ++ } ++ *dst++ = '\0'; ++} diff --git a/openwrt/target/linux/linux-2.4/patches/generic/603-netfilter_nat_pptp.patch b/openwrt/target/linux/linux-2.4/patches/generic/603-netfilter_nat_pptp.patch new file mode 100644 index 000000000..6c7d6e6f2 --- /dev/null +++ b/openwrt/target/linux/linux-2.4/patches/generic/603-netfilter_nat_pptp.patch @@ -0,0 +1,2412 @@ +diff -urN linux-2.4.29-old/include/linux/netfilter_ipv4/ip_conntrack.h linux-2.4.29-dev/include/linux/netfilter_ipv4/ip_conntrack.h +--- linux-2.4.29-old/include/linux/netfilter_ipv4/ip_conntrack.h 2005-04-02 19:04:11.000000000 +0200 ++++ linux-2.4.29-dev/include/linux/netfilter_ipv4/ip_conntrack.h 2005-04-02 19:41:05.000000000 +0200 +@@ -50,16 +50,19 @@ + + #include <linux/netfilter_ipv4/ip_conntrack_tcp.h> + #include <linux/netfilter_ipv4/ip_conntrack_icmp.h> ++#include <linux/netfilter_ipv4/ip_conntrack_proto_gre.h> + + /* per conntrack: protocol private data */ + union ip_conntrack_proto { + /* insert conntrack proto private data here */ + struct ip_ct_tcp tcp; + struct ip_ct_icmp icmp; ++ struct ip_ct_gre gre; + }; + + union ip_conntrack_expect_proto { + /* insert expect proto private data here */ ++ struct ip_ct_gre_expect gre; + }; + + /* Add protocol helper include file here */ +@@ -67,6 +70,7 @@ + + #include <linux/netfilter_ipv4/ip_conntrack_ftp.h> + #include <linux/netfilter_ipv4/ip_conntrack_irc.h> ++#include <linux/netfilter_ipv4/ip_conntrack_pptp.h> + + /* per expectation: application helper private data */ + union ip_conntrack_expect_help { +@@ -74,6 +78,7 @@ + struct ip_ct_amanda_expect exp_amanda_info; + struct ip_ct_ftp_expect exp_ftp_info; + struct ip_ct_irc_expect exp_irc_info; ++ struct ip_ct_pptp_expect exp_pptp_info; + + #ifdef CONFIG_IP_NF_NAT_NEEDED + union { +@@ -87,14 +92,17 @@ + /* insert conntrack helper private data (master) here */ + struct ip_ct_ftp_master ct_ftp_info; + struct ip_ct_irc_master ct_irc_info; ++ struct ip_ct_pptp_master ct_pptp_info; + }; + + #ifdef CONFIG_IP_NF_NAT_NEEDED + #include <linux/netfilter_ipv4/ip_nat.h> ++#include <linux/netfilter_ipv4/ip_nat_pptp.h> + + /* per conntrack: nat application helper private data */ + union ip_conntrack_nat_help { + /* insert nat helper private data here */ ++ struct ip_nat_pptp nat_pptp_info; + }; + #endif + +diff -urN linux-2.4.29-old/include/linux/netfilter_ipv4/ip_conntrack_pptp.h linux-2.4.29-dev/include/linux/netfilter_ipv4/ip_conntrack_pptp.h +--- linux-2.4.29-old/include/linux/netfilter_ipv4/ip_conntrack_pptp.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.4.29-dev/include/linux/netfilter_ipv4/ip_conntrack_pptp.h 2005-04-02 19:09:07.000000000 +0200 +@@ -0,0 +1,313 @@ ++/* PPTP constants and structs */ ++#ifndef _CONNTRACK_PPTP_H ++#define _CONNTRACK_PPTP_H ++ ++/* state of the control session */ ++enum pptp_ctrlsess_state { ++ PPTP_SESSION_NONE, /* no session present */ ++ PPTP_SESSION_ERROR, /* some session error */ ++ PPTP_SESSION_STOPREQ, /* stop_sess request seen */ ++ PPTP_SESSION_REQUESTED, /* start_sess request seen */ ++ PPTP_SESSION_CONFIRMED, /* session established */ ++}; ++ ++/* state of the call inside the control session */ ++enum pptp_ctrlcall_state { ++ PPTP_CALL_NONE, ++ PPTP_CALL_ERROR, ++ PPTP_CALL_OUT_REQ, ++ PPTP_CALL_OUT_CONF, ++ PPTP_CALL_IN_REQ, ++ PPTP_CALL_IN_REP, ++ PPTP_CALL_IN_CONF, ++ PPTP_CALL_CLEAR_REQ, ++}; ++ ++ ++/* conntrack private data */ ++struct ip_ct_pptp_master { ++ enum pptp_ctrlsess_state sstate; /* session state */ ++ ++ /* everything below is going to be per-expectation in newnat, ++ * since there could be more than one call within one session */ ++ enum pptp_ctrlcall_state cstate; /* call state */ ++ u_int16_t pac_call_id; /* call id of PAC, host byte order */ ++ u_int16_t pns_call_id; /* call id of PNS, host byte order */ ++}; ++ ++/* conntrack_expect private member */ ++struct ip_ct_pptp_expect { ++ enum pptp_ctrlcall_state cstate; /* call state */ ++ u_int16_t pac_call_id; /* call id of PAC */ ++ u_int16_t pns_call_id; /* call id of PNS */ ++}; ++ ++ ++#ifdef __KERNEL__ ++ ++#include <linux/netfilter_ipv4/lockhelp.h> ++DECLARE_LOCK_EXTERN(ip_pptp_lock); ++ ++#define IP_CONNTR_PPTP PPTP_CONTROL_PORT ++ ++union pptp_ctrl_union { ++ void *rawreq; ++ struct PptpStartSessionRequest *sreq; ++ struct PptpStartSessionReply *srep; ++ struct PptpStopSessionRequest *streq; ++ struct PptpStopSessionReply *strep; ++ struct PptpOutCallRequest *ocreq; ++ struct PptpOutCallReply *ocack; ++ struct PptpInCallRequest *icreq; ++ struct PptpInCallReply *icack; ++ struct PptpInCallConnected *iccon; ++ struct PptpClearCallRequest *clrreq; ++ struct PptpCallDisconnectNotify *disc; ++ struct PptpWanErrorNotify *wanerr; ++ struct PptpSetLinkInfo *setlink; ++}; ++ ++ ++ ++#define PPTP_CONTROL_PORT 1723 ++ ++#define PPTP_PACKET_CONTROL 1 ++#define PPTP_PACKET_MGMT 2 ++ ++#define PPTP_MAGIC_COOKIE 0x1a2b3c4d ++ ++struct pptp_pkt_hdr { ++ __u16 packetLength; ++ __u16 packetType; ++ __u32 magicCookie; ++}; ++ ++/* PptpControlMessageType values */ ++#define PPTP_START_SESSION_REQUEST 1 ++#define PPTP_START_SESSION_REPLY 2 ++#define PPTP_STOP_SESSION_REQUEST 3 ++#define PPTP_STOP_SESSION_REPLY 4 ++#define PPTP_ECHO_REQUEST 5 ++#define PPTP_ECHO_REPLY 6 ++#define PPTP_OUT_CALL_REQUEST 7 ++#define PPTP_OUT_CALL_REPLY 8 ++#define PPTP_IN_CALL_REQUEST 9 ++#define PPTP_IN_CALL_REPLY 10 ++#define PPTP_IN_CALL_CONNECT 11 ++#define PPTP_CALL_CLEAR_REQUEST 12 ++#define PPTP_CALL_DISCONNECT_NOTIFY 13 ++#define PPTP_WAN_ERROR_NOTIFY 14 ++#define PPTP_SET_LINK_INFO 15 ++ ++#define PPTP_MSG_MAX 15 ++ ++/* PptpGeneralError values */ ++#define PPTP_ERROR_CODE_NONE 0 ++#define PPTP_NOT_CONNECTED 1 ++#define PPTP_BAD_FORMAT 2 ++#define PPTP_BAD_VALUE 3 ++#define PPTP_NO_RESOURCE 4 ++#define PPTP_BAD_CALLID 5 ++#define PPTP_REMOVE_DEVICE_ERROR 6 ++ ++struct PptpControlHeader { ++ __u16 messageType; ++ __u16 reserved; ++}; ++ ++/* FramingCapability Bitmap Values */ ++#define PPTP_FRAME_CAP_ASYNC 0x1 ++#define PPTP_FRAME_CAP_SYNC 0x2 ++ ++/* BearerCapability Bitmap Values */ ++#define PPTP_BEARER_CAP_ANALOG 0x1 ++#define PPTP_BEARER_CAP_DIGITAL 0x2 ++ ++struct PptpStartSessionRequest { ++ __u16 protocolVersion; ++ __u8 reserved1; ++ __u8 reserved2; ++ __u32 framingCapability; ++ __u32 bearerCapability; ++ __u16 maxChannels; ++ __u16 firmwareRevision; ++ __u8 hostName[64]; ++ __u8 vendorString[64]; ++}; ++ ++/* PptpStartSessionResultCode Values */ ++#define PPTP_START_OK 1 ++#define PPTP_START_GENERAL_ERROR 2 ++#define PPTP_START_ALREADY_CONNECTED 3 ++#define PPTP_START_NOT_AUTHORIZED 4 ++#define PPTP_START_UNKNOWN_PROTOCOL 5 ++ ++struct PptpStartSessionReply { ++ __u16 protocolVersion; ++ __u8 resultCode; ++ __u8 generalErrorCode; ++ __u32 framingCapability; ++ __u32 bearerCapability; ++ __u16 maxChannels; ++ __u16 firmwareRevision; ++ __u8 hostName[64]; ++ __u8 vendorString[64]; ++}; ++ ++/* PptpStopReasons */ ++#define PPTP_STOP_NONE 1 ++#define PPTP_STOP_PROTOCOL 2 ++#define PPTP_STOP_LOCAL_SHUTDOWN 3 ++ ++struct PptpStopSessionRequest { ++ __u8 reason; ++}; ++ ++/* PptpStopSessionResultCode */ ++#define PPTP_STOP_OK 1 ++#define PPTP_STOP_GENERAL_ERROR 2 ++ ++struct PptpStopSessionReply { ++ __u8 resultCode; ++ __u8 generalErrorCode; ++}; ++ ++struct PptpEchoRequest { ++ __u32 identNumber; ++}; ++ ++/* PptpEchoReplyResultCode */ ++#define PPTP_ECHO_OK 1 ++#define PPTP_ECHO_GENERAL_ERROR 2 ++ ++struct PptpEchoReply { ++ __u32 identNumber; ++ __u8 resultCode; ++ __u8 generalErrorCode; ++ __u16 reserved; ++}; ++ ++/* PptpFramingType */ ++#define PPTP_ASYNC_FRAMING 1 ++#define PPTP_SYNC_FRAMING 2 ++#define PPTP_DONT_CARE_FRAMING 3 ++ ++/* PptpCallBearerType */ ++#define PPTP_ANALOG_TYPE 1 ++#define PPTP_DIGITAL_TYPE 2 ++#define PPTP_DONT_CARE_BEARER_TYPE 3 ++ ++struct PptpOutCallRequest { ++ __u16 callID; ++ __u16 callSerialNumber; ++ __u32 minBPS; ++ __u32 maxBPS; ++ __u32 bearerType; ++ __u32 framingType; ++ __u16 packetWindow; ++ __u16 packetProcDelay; ++ __u16 reserved1; ++ __u16 phoneNumberLength; ++ __u16 reserved2; ++ __u8 phoneNumber[64]; ++ __u8 subAddress[64]; ++}; ++ ++/* PptpCallResultCode */ ++#define PPTP_OUTCALL_CONNECT 1 ++#define PPTP_OUTCALL_GENERAL_ERROR 2 ++#define PPTP_OUTCALL_NO_CARRIER 3 ++#define PPTP_OUTCALL_BUSY 4 ++#define PPTP_OUTCALL_NO_DIAL_TONE 5 ++#define PPTP_OUTCALL_TIMEOUT 6 ++#define PPTP_OUTCALL_DONT_ACCEPT 7 ++ ++struct PptpOutCallReply { ++ __u16 callID; ++ __u16 peersCallID; ++ __u8 resultCode; ++ __u8 generalErrorCode; ++ __u16 causeCode; ++ __u32 connectSpeed; ++ __u16 packetWindow; ++ __u16 packetProcDelay; ++ __u32 physChannelID; ++}; ++ ++struct PptpInCallRequest { ++ __u16 callID; ++ __u16 callSerialNumber; ++ __u32 callBearerType; ++ __u32 physChannelID; ++ __u16 dialedNumberLength; ++ __u16 dialingNumberLength; ++ __u8 dialedNumber[64]; ++ __u8 dialingNumber[64]; ++ __u8 subAddress[64]; ++}; ++ ++/* PptpInCallResultCode */ ++#define PPTP_INCALL_ACCEPT 1 ++#define PPTP_INCALL_GENERAL_ERROR 2 ++#define PPTP_INCALL_DONT_ACCEPT 3 ++ ++struct PptpInCallReply { ++ __u16 callID; ++ __u16 peersCallID; ++ __u8 resultCode; ++ __u8 generalErrorCode; ++ __u16 packetWindow; ++ __u16 packetProcDelay; ++ __u16 reserved; ++}; ++ ++struct PptpInCallConnected { ++ __u16 peersCallID; ++ __u16 reserved; ++ __u32 connectSpeed; ++ __u16 packetWindow; ++ __u16 packetProcDelay; ++ __u32 callFramingType; ++}; ++ ++struct PptpClearCallRequest { ++ __u16 callID; ++ __u16 reserved; ++}; ++ ++struct PptpCallDisconnectNotify { ++ __u16 callID; ++ __u8 resultCode; ++ __u8 generalErrorCode; ++ __u16 causeCode; ++ __u16 reserved; ++ __u8 callStatistics[128]; ++}; ++ ++struct PptpWanErrorNotify { ++ __u16 peersCallID; ++ __u16 reserved; ++ __u32 crcErrors; ++ __u32 framingErrors; ++ __u32 hardwareOverRuns; ++ __u32 bufferOverRuns; ++ __u32 timeoutErrors; ++ __u32 alignmentErrors; ++}; ++ ++struct PptpSetLinkInfo { ++ __u16 peersCallID; ++ __u16 reserved; ++ __u32 sendAccm; ++ __u32 recvAccm; ++}; ++ ++ ++struct pptp_priv_data { ++ __u16 call_id; ++ __u16 mcall_id; ++ __u16 pcall_id; ++}; ++ ++#endif /* __KERNEL__ */ ++#endif /* _CONNTRACK_PPTP_H */ +diff -urN linux-2.4.29-old/include/linux/netfilter_ipv4/ip_conntrack_proto_gre.h linux-2.4.29-dev/include/linux/netfilter_ipv4/ip_conntrack_proto_gre.h +--- linux-2.4.29-old/include/linux/netfilter_ipv4/ip_conntrack_proto_gre.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.4.29-dev/include/linux/netfilter_ipv4/ip_conntrack_proto_gre.h 2005-04-02 19:10:45.000000000 +0200 +@@ -0,0 +1,123 @@ ++#ifndef _CONNTRACK_PROTO_GRE_H ++#define _CONNTRACK_PROTO_GRE_H ++#include <asm/byteorder.h> ++ ++/* GRE PROTOCOL HEADER */ ++ ++/* GRE Version field */ ++#define GRE_VERSION_1701 0x0 ++#define GRE_VERSION_PPTP 0x1 ++ ++/* GRE Protocol field */ ++#define GRE_PROTOCOL_PPTP 0x880B ++ ++/* GRE Flags */ ++#define GRE_FLAG_C 0x80 ++#define GRE_FLAG_R 0x40 ++#define GRE_FLAG_K 0x20 ++#define GRE_FLAG_S 0x10 ++#define GRE_FLAG_A 0x80 ++ ++#define GRE_IS_C(f) ((f)&GRE_FLAG_C) ++#define GRE_IS_R(f) ((f)&GRE_FLAG_R) ++#define GRE_IS_K(f) ((f)&GRE_FLAG_K) ++#define GRE_IS_S(f) ((f)&GRE_FLAG_S) ++#define GRE_IS_A(f) ((f)&GRE_FLAG_A) ++ ++/* GRE is a mess: Four different standards */ ++struct gre_hdr { ++#if defined(__LITTLE_ENDIAN_BITFIELD) ++ __u16 rec:3, ++ srr:1, ++ seq:1, ++ key:1, ++ routing:1, ++ csum:1, ++ version:3, ++ reserved:4, ++ ack:1; ++#elif defined(__BIG_ENDIAN_BITFIELD) ++ __u16 csum:1, ++ routing:1, ++ key:1, ++ seq:1, ++ srr:1, ++ rec:3, ++ ack:1, ++ reserved:4, ++ version:3; ++#else ++#error "Adjust your <asm/byteorder.h> defines" ++#endif ++ __u16 protocol; ++}; ++ ++/* modified GRE header for PPTP */ ++struct gre_hdr_pptp { ++ __u8 flags; /* bitfield */ ++ __u8 version; /* should be GRE_VERSION_PPTP */ ++ __u16 protocol; /* should be GRE_PROTOCOL_PPTP */ ++ __u16 payload_len; /* size of ppp payload, not inc. gre header */ ++ __u16 call_id; /* peer's call_id for this session */ ++ __u32 seq; /* sequence number. Present if S==1 */ ++ __u32 ack; /* seq number of highest packet recieved by */ ++ /* sender in this session */ ++}; ++ ++ ++/* this is part of ip_conntrack */ ++struct ip_ct_gre { ++ unsigned int stream_timeout; ++ unsigned int timeout; ++}; ++ ++/* this is part of ip_conntrack_expect */ ++struct ip_ct_gre_expect { ++ struct ip_ct_gre_keymap *keymap_orig, *keymap_reply; ++}; ++ ++#ifdef __KERNEL__ ++struct ip_conntrack_expect; ++ ++/* structure for original <-> reply keymap */ ++struct ip_ct_gre_keymap { ++ struct list_head list; ++ ++ struct ip_conntrack_tuple tuple; ++}; ++ ++ ++/* add new tuple->key_reply pair to keymap */ ++int ip_ct_gre_keymap_add(struct ip_conntrack_expect *exp, ++ struct ip_conntrack_tuple *t, ++ int reply); ++ ++/* change an existing keymap entry */ ++void ip_ct_gre_keymap_change(struct ip_ct_gre_keymap *km, ++ struct ip_conntrack_tuple *t); ++ ++/* delete keymap entries */ ++void ip_ct_gre_keymap_destroy(struct ip_conntrack_expect *exp); ++ ++ ++/* get pointer to gre key, if present */ ++static inline u_int32_t *gre_key(struct gre_hdr *greh) ++{ ++ if (!greh->key) ++ return NULL; ++ if (greh->csum || greh->routing) ++ return (u_int32_t *) (greh+sizeof(*greh)+4); ++ return (u_int32_t *) (greh+sizeof(*greh)); ++} ++ ++/* get pointer ot gre csum, if present */ ++static inline u_int16_t *gre_csum(struct gre_hdr *greh) ++{ ++ if (!greh->csum) ++ return NULL; ++ return (u_int16_t *) (greh+sizeof(*greh)); ++} ++ ++#endif /* __KERNEL__ */ ++ ++#endif /* _CONNTRACK_PROTO_GRE_H */ +diff -urN linux-2.4.29-old/include/linux/netfilter_ipv4/ip_conntrack_tuple.h linux-2.4.29-dev/include/linux/netfilter_ipv4/ip_conntrack_tuple.h +--- linux-2.4.29-old/include/linux/netfilter_ipv4/ip_conntrack_tuple.h 2003-11-28 19:26:21.000000000 +0100 ++++ linux-2.4.29-dev/include/linux/netfilter_ipv4/ip_conntrack_tuple.h 2005-04-02 19:07:18.000000000 +0200 +@@ -14,7 +14,7 @@ + union ip_conntrack_manip_proto + { + /* Add other protocols here. */ +- u_int16_t all; ++ u_int32_t all; + + struct { + u_int16_t port; +@@ -25,6 +25,9 @@ + struct { + u_int16_t id; + } icmp; ++ struct { ++ u_int32_t key; ++ } gre; + }; + + /* The manipulable part of the tuple. */ +@@ -44,7 +47,7 @@ + u_int32_t ip; + union { + /* Add other protocols here. */ +- u_int16_t all; ++ u_int32_t all; + + struct { + u_int16_t port; +@@ -55,6 +58,9 @@ + struct { + u_int8_t type, code; + } icmp; ++ struct { ++ u_int32_t key; ++ } gre; + } u; + + /* The protocol. */ +@@ -80,10 +86,16 @@ + #ifdef __KERNEL__ + + #define DUMP_TUPLE(tp) \ +-DEBUGP("tuple %p: %u %u.%u.%u.%u:%hu -> %u.%u.%u.%u:%hu\n", \ ++DEBUGP("tuple %p: %u %u.%u.%u.%u:%u -> %u.%u.%u.%u:%u\n", \ + (tp), (tp)->dst.protonum, \ +- NIPQUAD((tp)->src.ip), ntohs((tp)->src.u.all), \ +- NIPQUAD((tp)->dst.ip), ntohs((tp)->dst.u.all)) ++ NIPQUAD((tp)->src.ip), ntohl((tp)->src.u.all), \ ++ NIPQUAD((tp)->dst.ip), ntohl((tp)->dst.u.all)) ++ ++#define DUMP_TUPLE_RAW(x) \ ++ DEBUGP("tuple %p: %u %u.%u.%u.%u:0x%08x -> %u.%u.%u.%u:0x%08x\n",\ ++ (x), (x)->dst.protonum, \ ++ NIPQUAD((x)->src.ip), ntohl((x)->src.u.all), \ ++ NIPQUAD((x)->dst.ip), ntohl((x)->dst.u.all)) + + #define CTINFO2DIR(ctinfo) ((ctinfo) >= IP_CT_IS_REPLY ? IP_CT_DIR_REPLY : IP_CT_DIR_ORIGINAL) + +diff -urN linux-2.4.29-old/include/linux/netfilter_ipv4/ip_nat_pptp.h linux-2.4.29-dev/include/linux/netfilter_ipv4/ip_nat_pptp.h +--- linux-2.4.29-old/include/linux/netfilter_ipv4/ip_nat_pptp.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.4.29-dev/include/linux/netfilter_ipv4/ip_nat_pptp.h 2005-04-02 19:07:18.000000000 +0200 +@@ -0,0 +1,11 @@ ++/* PPTP constants and structs */ ++#ifndef _NAT_PPTP_H ++#define _NAT_PPTP_H ++ ++/* conntrack private data */ ++struct ip_nat_pptp { ++ u_int16_t pns_call_id; /* NAT'ed PNS call id */ ++ u_int16_t pac_call_id; /* NAT'ed PAC call id */ ++}; ++ ++#endif /* _NAT_PPTP_H */ +diff -urN linux-2.4.29-old/net/ipv4/netfilter/Config.in linux-2.4.29-dev/net/ipv4/netfilter/Config.in +--- linux-2.4.29-old/net/ipv4/netfilter/Config.in 2005-04-02 19:04:11.000000000 +0200 ++++ linux-2.4.29-dev/net/ipv4/netfilter/Config.in 2005-04-02 19:07:18.000000000 +0200 +@@ -10,6 +10,8 @@ + dep_tristate ' Amanda protocol support' CONFIG_IP_NF_AMANDA $CONFIG_IP_NF_CONNTRACK + dep_tristate ' TFTP protocol support' CONFIG_IP_NF_TFTP $CONFIG_IP_NF_CONNTRACK + dep_tristate ' IRC protocol support' CONFIG_IP_NF_IRC $CONFIG_IP_NF_CONNTRACK ++ dep_tristate ' GRE protocol support' CONFIG_IP_NF_CT_PROTO_GRE $CONFIG_IP_NF_CONNTRACK ++ dep_tristate ' PPTP protocol support' CONFIG_IP_NF_PPTP $CONFIG_IP_NF_CT_PROTO_GRE + fi + + if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then +@@ -67,6 +69,20 @@ + define_bool CONFIG_IP_NF_NAT_NEEDED y + dep_tristate ' MASQUERADE target support' CONFIG_IP_NF_TARGET_MASQUERADE $CONFIG_IP_NF_NAT + dep_tristate ' REDIRECT target support' CONFIG_IP_NF_TARGET_REDIRECT $CONFIG_IP_NF_NAT ++ if [ "$CONFIG_IP_NF_PPTP" = "m" ]; then ++ define_tristate CONFIG_IP_NF_NAT_PPTP m ++ else ++ if [ "$CONFIG_IP_NF_PPTP" = "y" ]; then ++ define_tristate CONFIG_IP_NF_NAT_PPTP $CONFIG_IP_NF_NAT ++ fi ++ fi ++ if [ "$CONFIG_IP_NF_CT_PROTO_GRE" = "m" ]; then ++ define_tristate CONFIG_IP_NF_NAT_PROTO_GRE m ++ else ++ if [ "$CONFIG_IP_NF_CT_PROTO_GRE" = "y" ]; then ++ define_tristate CONFIG_IP_NF_NAT_PROTO_GRE $CONFIG_IP_NF_NAT ++ fi ++ fi + if [ "$CONFIG_IP_NF_AMANDA" = "m" ]; then + define_tristate CONFIG_IP_NF_NAT_AMANDA m + else +diff -urN linux-2.4.29-old/net/ipv4/netfilter/ip_conntrack_core.c linux-2.4.29-dev/net/ipv4/netfilter/ip_conntrack_core.c +--- linux-2.4.29-old/net/ipv4/netfilter/ip_conntrack_core.c 2005-04-02 19:04:11.000000000 +0200 ++++ linux-2.4.29-dev/net/ipv4/netfilter/ip_conntrack_core.c 2005-04-02 19:07:18.000000000 +0200 +@@ -142,6 +142,8 @@ + tuple->dst.ip = iph->daddr; + tuple->dst.protonum = iph->protocol; + ++ tuple->src.u.all = tuple->dst.u.all = 0; ++ + ret = protocol->pkt_to_tuple((u_int32_t *)iph + iph->ihl, + len - 4*iph->ihl, + tuple); +@@ -157,6 +159,8 @@ + inverse->dst.ip = orig->src.ip; + inverse->dst.protonum = orig->dst.protonum; + ++ inverse->src.u.all = inverse->dst.u.all = 0; ++ + return protocol->invert_tuple(inverse, orig); + } + +@@ -954,8 +958,8 @@ + * so there is no need to use the tuple lock too */ + + DEBUGP("ip_conntrack_expect_related %p\n", related_to); +- DEBUGP("tuple: "); DUMP_TUPLE(&expect->tuple); +- DEBUGP("mask: "); DUMP_TUPLE(&expect->mask); ++ DEBUGP("tuple: "); DUMP_TUPLE_RAW(&expect->tuple); ++ DEBUGP("mask: "); DUMP_TUPLE_RAW(&expect->mask); + + old = LIST_FIND(&ip_conntrack_expect_list, resent_expect, + struct ip_conntrack_expect *, &expect->tuple, +@@ -1072,15 +1076,14 @@ + + MUST_BE_READ_LOCKED(&ip_conntrack_lock); + WRITE_LOCK(&ip_conntrack_expect_tuple_lock); +- + DEBUGP("change_expect:\n"); +- DEBUGP("exp tuple: "); DUMP_TUPLE(&expect->tuple); +- DEBUGP("exp mask: "); DUMP_TUPLE(&expect->mask); +- DEBUGP("newtuple: "); DUMP_TUPLE(newtuple); ++ DEBUGP("exp tuple: "); DUMP_TUPLE_RAW(&expect->tuple); ++ DEBUGP("exp mask: "); DUMP_TUPLE_RAW(&expect->mask); ++ DEBUGP("newtuple: "); DUMP_TUPLE_RAW(newtuple); + if (expect->ct_tuple.dst.protonum == 0) { + /* Never seen before */ + DEBUGP("change expect: never seen before\n"); +- if (!ip_ct_tuple_equal(&expect->tuple, newtuple) ++ if (!ip_ct_tuple_mask_cmp(&expect->tuple, newtuple, &expect->mask) + && LIST_FIND(&ip_conntrack_expect_list, expect_clash, + struct ip_conntrack_expect *, newtuple, &expect->mask)) { + /* Force NAT to find an unused tuple */ +diff -urN linux-2.4.29-old/net/ipv4/netfilter/ip_conntrack_pptp.c linux-2.4.29-dev/net/ipv4/netfilter/ip_conntrack_pptp.c +--- linux-2.4.29-old/net/ipv4/netfilter/ip_conntrack_pptp.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.4.29-dev/net/ipv4/netfilter/ip_conntrack_pptp.c 2005-04-02 19:13:21.000000000 +0200 +@@ -0,0 +1,633 @@ ++/* ++ * ip_conntrack_pptp.c - Version 1.9 ++ * ++ * Connection tracking support for PPTP (Point to Point Tunneling Protocol). ++ * PPTP is a a protocol for creating virtual private networks. ++ * It is a specification defined by Microsoft and some vendors ++ * working with Microsoft. PPTP is built on top of a modified ++ * version of the Internet Generic Routing Encapsulation Protocol. ++ * GRE is defined in RFC 1701 and RFC 1702. Documentation of ++ * PPTP can be found in RFC 2637 ++ * ++ * (C) 2000-2003 by Harald Welte <laforge@gnumonks.org> ++ * ++ * Development of this code funded by Astaro AG (http://www.astaro.com/) ++ * ++ * Limitations: ++ * - We blindly assume that control connections are always ++ * established in PNS->PAC direction. This is a violation ++ * of RFFC2673 ++ * ++ * TODO: - finish support for multiple calls within one session ++ * (needs expect reservations in newnat) ++ * - testing of incoming PPTP calls ++ * ++ * Changes: ++ * 2002-02-05 - Version 1.3 ++ * - Call ip_conntrack_unexpect_related() from ++ * pptp_timeout_related() to destroy expectations in case ++ * CALL_DISCONNECT_NOTIFY or tcp fin packet was seen ++ * (Philip Craig <philipc@snapgear.com>) ++ * - Add Version information at module loadtime ++ * 2002-02-10 - Version 1.6 ++ * - move to C99 style initializers ++ * - remove second expectation if first arrives ++ * ++ */ ++ ++#include <linux/config.h> ++#include <linux/module.h> ++#include <linux/netfilter.h> ++#include <linux/ip.h> ++#include <net/checksum.h> ++#include <net/tcp.h> ++ ++#include <linux/netfilter_ipv4/lockhelp.h> ++#include <linux/netfilter_ipv4/ip_conntrack_helper.h> ++#include <linux/netfilter_ipv4/ip_conntrack_proto_gre.h> ++#include <linux/netfilter_ipv4/ip_conntrack_pptp.h> ++ ++#define IP_CT_PPTP_VERSION "1.9" ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Harald Welte <laforge@gnumonks.org>"); ++MODULE_DESCRIPTION("Netfilter connection tracking helper module for PPTP"); ++ ++DECLARE_LOCK(ip_pptp_lock); ++ ++#if 0 ++#include "ip_conntrack_pptp_priv.h" ++#define DEBUGP(format, args...) printk(KERN_DEBUG __FILE__ ":" __FUNCTION__ \ ++ ": " format, ## args) ++#else ++#define DEBUGP(format, args...) ++#endif ++ ++#define SECS *HZ ++#define MINS * 60 SECS ++#define HOURS * 60 MINS ++#define DAYS * 24 HOURS ++ ++#define PPTP_GRE_TIMEOUT (10 MINS) ++#define PPTP_GRE_STREAM_TIMEOUT (5 DAYS) ++ ++static int pptp_expectfn(struct ip_conntrack *ct) ++{ ++ struct ip_conntrack *master; ++ struct ip_conntrack_expect *exp; ++ ++ DEBUGP("increasing timeouts\n"); ++ /* increase timeout of GRE data channel conntrack entry */ ++ ct->proto.gre.timeout = PPTP_GRE_TIMEOUT; ++ ct->proto.gre.stream_timeout = PPTP_GRE_STREAM_TIMEOUT; ++ ++ master = master_ct(ct); ++ if (!master) { ++ DEBUGP(" no master!!!\n"); ++ return 0; ++ } ++ ++ exp = ct->master; ++ if (!exp) { ++ DEBUGP("no expectation!!\n"); ++ return 0; ++ } ++ ++ DEBUGP("completing tuples with ct info\n"); ++ /* we can do this, since we're unconfirmed */ ++ if (ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.u.gre.key == ++ htonl(master->help.ct_pptp_info.pac_call_id)) { ++ /* assume PNS->PAC */ ++ ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.u.gre.key = ++ htonl(master->help.ct_pptp_info.pns_call_id); ++ ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.u.gre.key = ++ htonl(master->help.ct_pptp_info.pns_call_id); ++ } else { ++ /* assume PAC->PNS */ ++ ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.u.gre.key = ++ htonl(master->help.ct_pptp_info.pac_call_id); ++ ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.u.gre.key = ++ htonl(master->help.ct_pptp_info.pac_call_id); ++ } ++ ++ /* delete other expectation */ ++ if (exp->expected_list.next != &exp->expected_list) { ++ struct ip_conntrack_expect *other_exp; ++ struct list_head *cur_item, *next; ++ ++ for (cur_item = master->sibling_list.next; ++ cur_item != &master->sibling_list; cur_item = next) { ++ next = cur_item->next; ++ other_exp = list_entry(cur_item, ++ struct ip_conntrack_expect, ++ expected_list); ++ /* remove only if occurred at same sequence number */ ++ if (other_exp != exp && other_exp->seq == exp->seq) { ++ DEBUGP("unexpecting other direction\n"); ++ ip_ct_gre_keymap_destroy(other_exp); ++ ip_conntrack_unexpect_related(other_exp); ++ } ++ } ++ } ++ ++ return 0; ++} ++ ++/* timeout GRE data connections */ ++static int pptp_timeout_related(struct ip_conntrack *ct) ++{ ++ struct list_head *cur_item, *next; ++ struct ip_conntrack_expect *exp; ++ ++ /* FIXME: do we have to lock something ? */ ++ for (cur_item = ct->sibling_list.next; ++ cur_item != &ct->sibling_list; cur_item = next) { ++ next = cur_item->next; ++ exp = list_entry(cur_item, struct ip_conntrack_expect, ++ expected_list); ++ ++ ip_ct_gre_keymap_destroy(exp); ++ if (!exp->sibling) { ++ ip_conntrack_unexpect_related(exp); ++ continue; ++ } ++ ++ DEBUGP("setting timeout of conntrack %p to 0\n", ++ exp->sibling); ++ exp->sibling->proto.gre.timeout = 0; ++ exp->sibling->proto.gre.stream_timeout = 0; ++ ip_ct_refresh(exp->sibling, 0); ++ } ++ ++ return 0; ++} ++ ++/* expect GRE connections (PNS->PAC and PAC->PNS direction) */ ++static inline int ++exp_gre(struct ip_conntrack *master, ++ u_int32_t seq, ++ u_int16_t callid, ++ u_int16_t peer_callid) ++{ ++ struct ip_conntrack_expect exp; ++ struct ip_conntrack_tuple inv_tuple; ++ ++ memset(&exp, 0, sizeof(exp)); ++ /* tuple in original direction, PNS->PAC */ ++ exp.tuple.src.ip = master->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.ip; ++ exp.tuple.src.u.gre.key = htonl(ntohs(peer_callid)); ++ exp.tuple.dst.ip = master->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.ip; ++ exp.tuple.dst.u.gre.key = htonl(ntohs(callid)); ++ exp.tuple.dst.protonum = IPPROTO_GRE; ++ ++ exp.mask.src.ip = 0xffffffff; ++ exp.mask.src.u.all = 0; ++ exp.mask.dst.u.all = 0; ++ exp.mask.dst.u.gre.key = 0xffffffff; ++ exp.mask.dst.ip = 0xffffffff; ++ exp.mask.dst.protonum = 0xffff; ++ ++ exp.seq = seq; ++ exp.expectfn = pptp_expectfn; ++ ++ exp.help.exp_pptp_info.pac_call_id = ntohs(callid); ++ exp.help.exp_pptp_info.pns_call_id = ntohs(peer_callid); ++ ++ DEBUGP("calling expect_related "); ++ DUMP_TUPLE_RAW(&exp.tuple); ++ ++ /* Add GRE keymap entries */ ++ if (ip_ct_gre_keymap_add(&exp, &exp.tuple, 0) != 0) ++ return 1; ++ ++ invert_tuplepr(&inv_tuple, &exp.tuple); ++ if (ip_ct_gre_keymap_add(&exp, &inv_tuple, 1) != 0) { ++ ip_ct_gre_keymap_destroy(&exp); ++ return 1; ++ } ++ ++ if (ip_conntrack_expect_related(master, &exp) != 0) { ++ ip_ct_gre_keymap_destroy(&exp); ++ DEBUGP("cannot expect_related()\n"); ++ return 1; ++ } ++ ++ /* tuple in reply direction, PAC->PNS */ ++ exp.tuple.src.ip = master->tuplehash[IP_CT_DIR_REPLY].tuple.src.ip; ++ exp.tuple.src.u.gre.key = htonl(ntohs(callid)); ++ exp.tuple.dst.ip = master->tuplehash[IP_CT_DIR_REPLY].tuple.dst.ip; ++ exp.tuple.dst.u.gre.key = htonl(ntohs(peer_callid)); ++ ++ DEBUGP("calling expect_related "); ++ DUMP_TUPLE_RAW(&exp.tuple); ++ ++ /* Add GRE keymap entries */ ++ ip_ct_gre_keymap_add(&exp, &exp.tuple, 0); ++ invert_tuplepr(&inv_tuple, &exp.tuple); ++ ip_ct_gre_keymap_add(&exp, &inv_tuple, 1); ++ /* FIXME: cannot handle error correctly, since we need to free ++ * the above keymap :( */ ++ ++ if (ip_conntrack_expect_related(master, &exp) != 0) { ++ /* free the second pair of keypmaps */ ++ ip_ct_gre_keymap_destroy(&exp); ++ DEBUGP("cannot expect_related():\n"); ++ return 1; ++ } ++ ++ return 0; ++} ++ ++static inline int ++pptp_inbound_pkt(struct tcphdr *tcph, ++ struct pptp_pkt_hdr *pptph, ++ size_t datalen, ++ struct ip_conntrack *ct, ++ enum ip_conntrack_info ctinfo) ++{ ++ struct PptpControlHeader *ctlh; ++ union pptp_ctrl_union pptpReq; ++ ++ struct ip_ct_pptp_master *info = &ct->help.ct_pptp_info; ++ u_int16_t msg, *cid, *pcid; ++ u_int32_t seq; ++ ++ ctlh = (struct PptpControlHeader *) ++ ((char *) pptph + sizeof(struct pptp_pkt_hdr)); ++ pptpReq.rawreq = (void *) ++ ((char *) ctlh + sizeof(struct PptpControlHeader)); ++ ++ msg = ntohs(ctlh->messageType); ++ DEBUGP("inbound control message %s\n", strMName[msg]); ++ ++ switch (msg) { ++ case PPTP_START_SESSION_REPLY: ++ /* server confirms new control session */ ++ if (info->sstate < PPTP_SESSION_REQUESTED) { ++ DEBUGP("%s without START_SESS_REQUEST\n", ++ strMName[msg]); ++ break; ++ } ++ if (pptpReq.srep->resultCode == PPTP_START_OK) ++ info->sstate = PPTP_SESSION_CONFIRMED; ++ else ++ info->sstate = PPTP_SESSION_ERROR; ++ break; ++ ++ case PPTP_STOP_SESSION_REPLY: ++ /* server confirms end of control session */ ++ if (info->sstate > PPTP_SESSION_STOPREQ) { ++ DEBUGP("%s without STOP_SESS_REQUEST\n", ++ strMName[msg]); ++ break; ++ } ++ if (pptpReq.strep->resultCode == PPTP_STOP_OK) ++ info->sstate = PPTP_SESSION_NONE; ++ else ++ info->sstate = PPTP_SESSION_ERROR; ++ break; ++ ++ case PPTP_OUT_CALL_REPLY: ++ /* server accepted call, we now expect GRE frames */ ++ if (info->sstate != PPTP_SESSION_CONFIRMED) { ++ DEBUGP("%s but no session\n", strMName[msg]); ++ break; ++ } ++ if (info->cstate != PPTP_CALL_OUT_REQ && ++ info->cstate != PPTP_CALL_OUT_CONF) { ++ DEBUGP("%s without OUTCALL_REQ\n", strMName[msg]); ++ break; ++ } ++ if (pptpReq.ocack->resultCode != PPTP_OUTCALL_CONNECT) { ++ info->cstate = PPTP_CALL_NONE; ++ break; ++ } ++ ++ cid = &pptpReq.ocack->callID; ++ pcid = &pptpReq.ocack->peersCallID; ++ ++ info->pac_call_id = ntohs(*cid); ++ ++ if (htons(info->pns_call_id) != *pcid) { ++ DEBUGP("%s for unknown callid %u\n", ++ strMName[msg], ntohs(*pcid)); ++ break; ++ } ++ ++ DEBUGP("%s, CID=%X, PCID=%X\n", strMName[msg], ++ ntohs(*cid), ntohs(*pcid)); ++ ++ info->cstate = PPTP_CALL_OUT_CONF; ++ ++ seq = ntohl(tcph->seq) + ((void *)pcid - (void *)pptph); ++ if (exp_gre(ct, seq, *cid, *pcid) != 0) ++ printk("ip_conntrack_pptp: error during exp_gre\n"); ++ break; ++ ++ case PPTP_IN_CALL_REQUEST: ++ /* server tells us about incoming call request */ ++ if (info->sstate != PPTP_SESSION_CONFIRMED) { ++ DEBUGP("%s but no session\n", strMName[msg]); ++ break; ++ } ++ pcid = &pptpReq.icack->peersCallID; ++ DEBUGP("%s, PCID=%X\n", strMName[msg], ntohs(*pcid)); ++ info->cstate = PPTP_CALL_IN_REQ; ++ info->pac_call_id= ntohs(*pcid); ++ break; ++ ++ case PPTP_IN_CALL_CONNECT: ++ /* server tells us about incoming call established */ ++ if (info->sstate != PPTP_SESSION_CONFIRMED) { ++ DEBUGP("%s but no session\n", strMName[msg]); ++ break; ++ } ++ if (info->sstate != PPTP_CALL_IN_REP ++ && info->sstate != PPTP_CALL_IN_CONF) { ++ DEBUGP("%s but never sent IN_CALL_REPLY\n", ++ strMName[msg]); ++ break; ++ } ++ ++ pcid = &pptpReq.iccon->peersCallID; ++ cid = &info->pac_call_id; ++ ++ if (info->pns_call_id != ntohs(*pcid)) { ++ DEBUGP("%s for unknown CallID %u\n", ++ strMName[msg], ntohs(*cid)); ++ break; ++ } ++ ++ DEBUGP("%s, PCID=%X\n", strMName[msg], ntohs(*pcid)); ++ info->cstate = PPTP_CALL_IN_CONF; ++ ++ /* we expect a GRE connection from PAC to PNS */ ++ seq = ntohl(tcph->seq) + ((void *)pcid - (void *)pptph); ++ if (exp_gre(ct, seq, *cid, *pcid) != 0) ++ printk("ip_conntrack_pptp: error during exp_gre\n"); ++ ++ break; ++ ++ case PPTP_CALL_DISCONNECT_NOTIFY: ++ /* server confirms disconnect */ ++ cid = &pptpReq.disc->callID; ++ DEBUGP("%s, CID=%X\n", strMName[msg], ntohs(*cid)); ++ info->cstate = PPTP_CALL_NONE; ++ ++ /* untrack this call id, unexpect GRE packets */ ++ pptp_timeout_related(ct); ++ break; ++ ++ case PPTP_WAN_ERROR_NOTIFY: ++ break; ++ ++ case PPTP_ECHO_REQUEST: ++ case PPTP_ECHO_REPLY: ++ /* I don't have to explain these ;) */ ++ break; ++ default: ++ DEBUGP("invalid %s (TY=%d)\n", (msg <= PPTP_MSG_MAX) ++ ? strMName[msg]:strMName[0], msg); ++ break; ++ } ++ ++ return NF_ACCEPT; ++ ++} ++ ++static inline int ++pptp_outbound_pkt(struct tcphdr *tcph, ++ struct pptp_pkt_hdr *pptph, ++ size_t datalen, ++ struct ip_conntrack *ct, ++ enum ip_conntrack_info ctinfo) ++{ ++ struct PptpControlHeader *ctlh; ++ union pptp_ctrl_union pptpReq; ++ struct ip_ct_pptp_master *info = &ct->help.ct_pptp_info; ++ u_int16_t msg, *cid, *pcid; ++ ++ ctlh = (struct PptpControlHeader *) ((void *) pptph + sizeof(*pptph)); ++ pptpReq.rawreq = (void *) ((void *) ctlh + sizeof(*ctlh)); ++ ++ msg = ntohs(ctlh->messageType); ++ DEBUGP("outbound control message %s\n", strMName[msg]); ++ ++ switch (msg) { ++ case PPTP_START_SESSION_REQUEST: ++ /* client requests for new control session */ ++ if (info->sstate != PPTP_SESSION_NONE) { ++ DEBUGP("%s but we already have one", ++ strMName[msg]); ++ } ++ info->sstate = PPTP_SESSION_REQUESTED; ++ break; ++ case PPTP_STOP_SESSION_REQUEST: ++ /* client requests end of control session */ ++ info->sstate = PPTP_SESSION_STOPREQ; ++ break; ++ ++ case PPTP_OUT_CALL_REQUEST: ++ /* client initiating connection to server */ ++ if (info->sstate != PPTP_SESSION_CONFIRMED) { ++ DEBUGP("%s but no session\n", ++ strMName[msg]); ++ break; ++ } ++ info->cstate = PPTP_CALL_OUT_REQ; ++ /* track PNS call id */ ++ cid = &pptpReq.ocreq->callID; ++ DEBUGP("%s, CID=%X\n", strMName[msg], ntohs(*cid)); ++ info->pns_call_id = ntohs(*cid); ++ break; ++ case PPTP_IN_CALL_REPLY: ++ /* client answers incoming call */ ++ if (info->cstate != PPTP_CALL_IN_REQ ++ && info->cstate != PPTP_CALL_IN_REP) { ++ DEBUGP("%s without incall_req\n", ++ strMName[msg]); ++ break; ++ } ++ if (pptpReq.icack->resultCode != PPTP_INCALL_ACCEPT) { ++ info->cstate = PPTP_CALL_NONE; ++ break; ++ } ++ pcid = &pptpReq.icack->peersCallID; ++ if (info->pac_call_id != ntohs(*pcid)) { ++ DEBUGP("%s for unknown call %u\n", ++ strMName[msg], ntohs(*pcid)); ++ break; ++ } ++ DEBUGP("%s, CID=%X\n", strMName[msg], ntohs(*pcid)); ++ /* part two of the three-way handshake */ ++ info->cstate = PPTP_CALL_IN_REP; ++ info->pns_call_id = ntohs(pptpReq.icack->callID); ++ break; ++ ++ case PPTP_CALL_CLEAR_REQUEST: ++ /* client requests hangup of call */ ++ if (info->sstate != PPTP_SESSION_CONFIRMED) { ++ DEBUGP("CLEAR_CALL but no session\n"); ++ break; ++ } ++ /* FUTURE: iterate over all calls and check if ++ * call ID is valid. We don't do this without newnat, ++ * because we only know about last call */ ++ info->cstate = PPTP_CALL_CLEAR_REQ; ++ break; ++ case PPTP_SET_LINK_INFO: ++ break; ++ case PPTP_ECHO_REQUEST: ++ case PPTP_ECHO_REPLY: ++ /* I don't have to explain these ;) */ ++ break; ++ default: ++ DEBUGP("invalid %s (TY=%d)\n", (msg <= PPTP_MSG_MAX)? ++ strMName[msg]:strMName[0], msg); ++ /* unknown: no need to create GRE masq table entry */ ++ break; ++ } ++ ++ return NF_ACCEPT; ++} ++ ++ ++/* track caller id inside control connection, call expect_related */ ++static int ++conntrack_pptp_help(const struct iphdr *iph, size_t len, ++ struct ip_conntrack *ct, enum ip_conntrack_info ctinfo) ++ ++{ ++ struct pptp_pkt_hdr *pptph; ++ ++ struct tcphdr *tcph = (void *) iph + iph->ihl * 4; ++ u_int32_t tcplen = len - iph->ihl * 4; ++ u_int32_t datalen = tcplen - tcph->doff * 4; ++ void *datalimit; ++ int dir = CTINFO2DIR(ctinfo); ++ struct ip_ct_pptp_master *info = &ct->help.ct_pptp_info; ++ ++ int oldsstate, oldcstate; ++ int ret; ++ ++ /* don't do any tracking before tcp handshake complete */ ++ if (ctinfo != IP_CT_ESTABLISHED ++ && ctinfo != IP_CT_ESTABLISHED+IP_CT_IS_REPLY) { ++ DEBUGP("ctinfo = %u, skipping\n", ctinfo); ++ return NF_ACCEPT; ++ } ++ ++ /* not a complete TCP header? */ ++ if (tcplen < sizeof(struct tcphdr) || tcplen < tcph->doff * 4) { ++ DEBUGP("tcplen = %u\n", tcplen); ++ return NF_ACCEPT; ++ } ++ ++ /* checksum invalid? */ ++ if (tcp_v4_check(tcph, tcplen, iph->saddr, iph->daddr, ++ csum_partial((char *) tcph, tcplen, 0))) { ++ printk(KERN_NOTICE __FILE__ ": bad csum\n"); ++ /* W2K PPTP server sends TCP packets with wrong checksum :(( */ ++ //return NF_ACCEPT; ++ } ++ ++ if (tcph->fin || tcph->rst) { ++ DEBUGP("RST/FIN received, timeouting GRE\n"); ++ /* can't do this after real newnat */ ++ info->cstate = PPTP_CALL_NONE; ++ ++ /* untrack this call id, unexpect GRE packets */ ++ pptp_timeout_related(ct); ++ } ++ ++ ++ pptph = (struct pptp_pkt_hdr *) ((void *) tcph + tcph->doff * 4); ++ datalimit = (void *) pptph + datalen; ++ ++ /* not a full pptp packet header? */ ++ if ((void *) pptph+sizeof(*pptph) >= datalimit) { ++ DEBUGP("no full PPTP header, can't track\n"); ++ return NF_ACCEPT; ++ } ++ ++ /* if it's not a control message we can't do anything with it */ ++ if (ntohs(pptph->packetType) != PPTP_PACKET_CONTROL || ++ ntohl(pptph->magicCookie) != PPTP_MAGIC_COOKIE) { ++ DEBUGP("not a control packet\n"); ++ return NF_ACCEPT; ++ } ++ ++ oldsstate = info->sstate; ++ oldcstate = info->cstate; ++ ++ LOCK_BH(&ip_pptp_lock); ++ ++ /* FIXME: We just blindly assume that the control connection is always ++ * established from PNS->PAC. However, RFC makes no guarantee */ ++ if (dir == IP_CT_DIR_ORIGINAL) ++ /* client -> server (PNS -> PAC) */ ++ ret = pptp_outbound_pkt(tcph, pptph, datalen, ct, ctinfo); ++ else ++ /* server -> client (PAC -> PNS) */ ++ ret = pptp_inbound_pkt(tcph, pptph, datalen, ct, ctinfo); ++ DEBUGP("sstate: %d->%d, cstate: %d->%d\n", ++ oldsstate, info->sstate, oldcstate, info->cstate); ++ UNLOCK_BH(&ip_pptp_lock); ++ ++ return ret; ++} ++ ++/* control protocol helper */ ++static struct ip_conntrack_helper pptp = { ++ .list = { NULL, NULL }, ++ .name = "pptp", ++ .flags = IP_CT_HELPER_F_REUSE_EXPECT, ++ .me = THIS_MODULE, ++ .max_expected = 2, ++ .timeout = 0, ++ .tuple = { .src = { .ip = 0, ++ .u = { .tcp = { .port = ++ __constant_htons(PPTP_CONTROL_PORT) } } ++ }, ++ .dst = { .ip = 0, ++ .u = { .all = 0 }, ++ .protonum = IPPROTO_TCP ++ } ++ }, ++ .mask = { .src = { .ip = 0, ++ .u = { .tcp = { .port = 0xffff } } ++ }, ++ .dst = { .ip = 0, ++ .u = { .all = 0 }, ++ .protonum = 0xffff ++ } ++ }, ++ .help = conntrack_pptp_help ++}; ++ ++/* ip_conntrack_pptp initialization */ ++static int __init init(void) ++{ ++ int retcode; ++ ++ DEBUGP(__FILE__ ": registering helper\n"); ++ if ((retcode = ip_conntrack_helper_register(&pptp))) { ++ printk(KERN_ERR "Unable to register conntrack application " ++ "helper for pptp: %d\n", retcode); ++ return -EIO; ++ } ++ ++ printk("ip_conntrack_pptp version %s loaded\n", IP_CT_PPTP_VERSION); ++ return 0; ++} ++ ++static void __exit fini(void) ++{ ++ ip_conntrack_helper_unregister(&pptp); ++ printk("ip_conntrack_pptp version %s unloaded\n", IP_CT_PPTP_VERSION); ++} ++ ++module_init(init); ++module_exit(fini); ++ ++EXPORT_SYMBOL(ip_pptp_lock); +diff -urN linux-2.4.29-old/net/ipv4/netfilter/ip_conntrack_pptp_priv.h linux-2.4.29-dev/net/ipv4/netfilter/ip_conntrack_pptp_priv.h +--- linux-2.4.29-old/net/ipv4/netfilter/ip_conntrack_pptp_priv.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.4.29-dev/net/ipv4/netfilter/ip_conntrack_pptp_priv.h 2005-04-02 19:14:10.000000000 +0200 +@@ -0,0 +1,24 @@ ++#ifndef _IP_CT_PPTP_PRIV_H ++#define _IP_CT_PPTP_PRIV_H ++ ++/* PptpControlMessageType names */ ++static const char *strMName[] = { ++ "UNKNOWN_MESSAGE", ++ "START_SESSION_REQUEST", ++ "START_SESSION_REPLY", ++ "STOP_SESSION_REQUEST", ++ "STOP_SESSION_REPLY", ++ "ECHO_REQUEST", ++ "ECHO_REPLY", ++ "OUT_CALL_REQUEST", ++ "OUT_CALL_REPLY", ++ "IN_CALL_REQUEST", ++ "IN_CALL_REPLY", ++ "IN_CALL_CONNECT", ++ "CALL_CLEAR_REQUEST", ++ "CALL_DISCONNECT_NOTIFY", ++ "WAN_ERROR_NOTIFY", ++ "SET_LINK_INFO" ++}; ++ ++#endif +diff -urN linux-2.4.29-old/net/ipv4/netfilter/ip_conntrack_proto_gre.c linux-2.4.29-dev/net/ipv4/netfilter/ip_conntrack_proto_gre.c +--- linux-2.4.29-old/net/ipv4/netfilter/ip_conntrack_proto_gre.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.4.29-dev/net/ipv4/netfilter/ip_conntrack_proto_gre.c 2005-04-02 19:14:31.000000000 +0200 +@@ -0,0 +1,333 @@ ++/* ++ * ip_conntrack_proto_gre.c - Version 1.2 ++ * ++ * Connection tracking protocol helper module for GRE. ++ * ++ * GRE is a generic encapsulation protocol, which is generally not very ++ * suited for NAT, as it has no protocol-specific part as port numbers. ++ * ++ * It has an optional key field, which may help us distinguishing two ++ * connections between the same two hosts. ++ * ++ * GRE is defined in RFC 1701 and RFC 1702, as well as RFC 2784 ++ * ++ * PPTP is built on top of a modified version of GRE, and has a mandatory ++ * field called "CallID", which serves us for the same purpose as the key ++ * field in plain GRE. ++ * ++ * Documentation about PPTP can be found in RFC 2637 ++ * ++ * (C) 2000-2003 by Harald Welte <laforge@gnumonks.org> ++ * ++ * Development of this code funded by Astaro AG (http://www.astaro.com/) ++ * ++ */ ++ ++#include <linux/config.h> ++#include <linux/module.h> ++#include <linux/types.h> ++#include <linux/timer.h> ++#include <linux/netfilter.h> ++#include <linux/ip.h> ++#include <linux/in.h> ++#include <linux/list.h> ++ ++#include <linux/netfilter_ipv4/lockhelp.h> ++ ++DECLARE_RWLOCK(ip_ct_gre_lock); ++#define ASSERT_READ_LOCK(x) MUST_BE_READ_LOCKED(&ip_ct_gre_lock) ++#define ASSERT_WRITE_LOCK(x) MUST_BE_WRITE_LOCKED(&ip_ct_gre_lock) ++ ++#include <linux/netfilter_ipv4/listhelp.h> ++#include <linux/netfilter_ipv4/ip_conntrack_protocol.h> ++#include <linux/netfilter_ipv4/ip_conntrack_helper.h> ++#include <linux/netfilter_ipv4/ip_conntrack_core.h> ++ ++#include <linux/netfilter_ipv4/ip_conntrack_proto_gre.h> ++#include <linux/netfilter_ipv4/ip_conntrack_pptp.h> ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Harald Welte <laforge@gnumonks.org>"); ++MODULE_DESCRIPTION("netfilter connection tracking protocol helper for GRE"); ++ ++/* shamelessly stolen from ip_conntrack_proto_udp.c */ ++#define GRE_TIMEOUT (30*HZ) ++#define GRE_STREAM_TIMEOUT (180*HZ) ++ ++#if 0 ++#define DEBUGP(format, args...) printk(KERN_DEBUG __FILE__ ":" __FUNCTION__ \ ++ ": " format, ## args) ++#define DUMP_TUPLE_GRE(x) printk("%u.%u.%u.%u:0x%x -> %u.%u.%u.%u:0x%x:%u:0x%x\n", \ ++ NIPQUAD((x)->src.ip), ntohl((x)->src.u.gre.key), \ ++ NIPQUAD((x)->dst.ip), ntohl((x)->dst.u.gre.key)) ++#else ++#define DEBUGP(x, args...) ++#define DUMP_TUPLE_GRE(x) ++#endif ++ ++/* GRE KEYMAP HANDLING FUNCTIONS */ ++static LIST_HEAD(gre_keymap_list); ++ ++static inline int gre_key_cmpfn(const struct ip_ct_gre_keymap *km, ++ const struct ip_conntrack_tuple *t) ++{ ++ return ((km->tuple.src.ip == t->src.ip) && ++ (km->tuple.dst.ip == t->dst.ip) && ++ (km->tuple.dst.protonum == t->dst.protonum) && ++ (km->tuple.dst.u.all == t->dst.u.all)); ++} ++ ++/* look up the source key for a given tuple */ ++static u_int32_t gre_keymap_lookup(struct ip_conntrack_tuple *t) ++{ ++ struct ip_ct_gre_keymap *km; ++ u_int32_t key; ++ ++ READ_LOCK(&ip_ct_gre_lock); ++ km = LIST_FIND(&gre_keymap_list, gre_key_cmpfn, ++ struct ip_ct_gre_keymap *, t); ++ if (!km) { ++ READ_UNLOCK(&ip_ct_gre_lock); ++ return 0; ++ } ++ ++ key = km->tuple.src.u.gre.key; ++ READ_UNLOCK(&ip_ct_gre_lock); ++ ++ return key; ++} ++ ++/* add a single keymap entry, associate with specified expect */ ++int ip_ct_gre_keymap_add(struct ip_conntrack_expect *exp, ++ struct ip_conntrack_tuple *t, int reply) ++{ ++ struct ip_ct_gre_keymap *km; ++ ++ km = kmalloc(sizeof(*km), GFP_ATOMIC); ++ if (!km) ++ return -1; ++ ++ /* initializing list head should be sufficient */ ++ memset(km, 0, sizeof(*km)); ++ ++ memcpy(&km->tuple, t, sizeof(*t)); ++ ++ if (!reply) ++ exp->proto.gre.keymap_orig = km; ++ else ++ exp->proto.gre.keymap_reply = km; ++ ++ DEBUGP("adding new entry %p: ", km); ++ DUMP_TUPLE_GRE(&km->tuple); ++ ++ WRITE_LOCK(&ip_ct_gre_lock); ++ list_append(&gre_keymap_list, km); ++ WRITE_UNLOCK(&ip_ct_gre_lock); ++ ++ return 0; ++} ++ ++/* change the tuple of a keymap entry (used by nat helper) */ ++void ip_ct_gre_keymap_change(struct ip_ct_gre_keymap *km, ++ struct ip_conntrack_tuple *t) ++{ ++ DEBUGP("changing entry %p to: ", km); ++ DUMP_TUPLE_GRE(t); ++ ++ WRITE_LOCK(&ip_ct_gre_lock); ++ memcpy(&km->tuple, t, sizeof(km->tuple)); ++ WRITE_UNLOCK(&ip_ct_gre_lock); ++} ++ ++/* destroy the keymap entries associated with specified expect */ ++void ip_ct_gre_keymap_destroy(struct ip_conntrack_expect *exp) ++{ ++ DEBUGP("entering for exp %p\n", exp); ++ WRITE_LOCK(&ip_ct_gre_lock); ++ if (exp->proto.gre.keymap_orig) { ++ DEBUGP("removing %p from list\n", exp->proto.gre.keymap_orig); ++ list_del(&exp->proto.gre.keymap_orig->list); ++ kfree(exp->proto.gre.keymap_orig); ++ exp->proto.gre.keymap_orig = NULL; ++ } ++ if (exp->proto.gre.keymap_reply) { ++ DEBUGP("removing %p from list\n", exp->proto.gre.keymap_reply); ++ list_del(&exp->proto.gre.keymap_reply->list); ++ kfree(exp->proto.gre.keymap_reply); ++ exp->proto.gre.keymap_reply = NULL; ++ } ++ WRITE_UNLOCK(&ip_ct_gre_lock); ++} ++ ++ ++/* PUBLIC CONNTRACK PROTO HELPER FUNCTIONS */ ++ ++/* invert gre part of tuple */ ++static int gre_invert_tuple(struct ip_conntrack_tuple *tuple, ++ const struct ip_conntrack_tuple *orig) ++{ ++ tuple->dst.u.gre.key = orig->src.u.gre.key; ++ tuple->src.u.gre.key = orig->dst.u.gre.key; ++ ++ return 1; ++} ++ ++/* gre hdr info to tuple */ ++static int gre_pkt_to_tuple(const void *datah, size_t datalen, ++ struct ip_conntrack_tuple *tuple) ++{ ++ struct gre_hdr *grehdr = (struct gre_hdr *) datah; ++ struct gre_hdr_pptp *pgrehdr = (struct gre_hdr_pptp *) datah; ++ u_int32_t srckey; ++ ++ /* core guarantees 8 protocol bytes, no need for size check */ ++ ++ switch (grehdr->version) { ++ case GRE_VERSION_1701: ++ if (!grehdr->key) { ++ DEBUGP("Can't track GRE without key\n"); ++ return 0; ++ } ++ tuple->dst.u.gre.key = *(gre_key(grehdr)); ++ break; ++ ++ case GRE_VERSION_PPTP: ++ if (ntohs(grehdr->protocol) != GRE_PROTOCOL_PPTP) { ++ DEBUGP("GRE_VERSION_PPTP but unknown proto\n"); ++ return 0; ++ } ++ tuple->dst.u.gre.key = htonl(ntohs(pgrehdr->call_id)); ++ break; ++ ++ default: ++ printk(KERN_WARNING "unknown GRE version %hu\n", ++ grehdr->version); ++ return 0; ++ } ++ ++ srckey = gre_keymap_lookup(tuple); ++ ++#if 0 ++ DEBUGP("found src key %x for tuple ", ntohl(srckey)); ++ DUMP_TUPLE_GRE(tuple); ++#endif ++ tuple->src.u.gre.key = srckey; ++ ++ return 1; ++} ++ ++/* print gre part of tuple */ ++static unsigned int gre_print_tuple(char *buffer, ++ const struct ip_conntrack_tuple *tuple) ++{ ++ return sprintf(buffer, "srckey=0x%x dstkey=0x%x ", ++ ntohl(tuple->src.u.gre.key), ++ ntohl(tuple->dst.u.gre.key)); ++} ++ ++/* print private data for conntrack */ ++static unsigned int gre_print_conntrack(char *buffer, ++ const struct ip_conntrack *ct) ++{ ++ return sprintf(buffer, "timeout=%u, stream_timeout=%u ", ++ (ct->proto.gre.timeout / HZ), ++ (ct->proto.gre.stream_timeout / HZ)); ++} ++ ++/* Returns verdict for packet, and may modify conntrack */ ++static int gre_packet(struct ip_conntrack *ct, ++ struct iphdr *iph, size_t len, ++ enum ip_conntrack_info conntrackinfo) ++{ ++ /* If we've seen traffic both ways, this is a GRE connection. ++ * Extend timeout. */ ++ if (ct->status & IPS_SEEN_REPLY) { ++ ip_ct_refresh(ct, ct->proto.gre.stream_timeout); ++ /* Also, more likely to be important, and not a probe. */ ++ set_bit(IPS_ASSURED_BIT, &ct->status); ++ } else ++ ip_ct_refresh(ct, ct->proto.gre.timeout); ++ ++ return NF_ACCEPT; ++} ++ ++/* Called when a new connection for this protocol found. */ ++static int gre_new(struct ip_conntrack *ct, ++ struct iphdr *iph, size_t len) ++{ ++ DEBUGP(": "); ++ DUMP_TUPLE_GRE(&ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple); ++ ++ /* initialize to sane value. Ideally a conntrack helper ++ * (e.g. in case of pptp) is increasing them */ ++ ct->proto.gre.stream_timeout = GRE_STREAM_TIMEOUT; ++ ct->proto.gre.timeout = GRE_TIMEOUT; ++ ++ return 1; ++} ++ ++/* Called when a conntrack entry has already been removed from the hashes ++ * and is about to be deleted from memory */ ++static void gre_destroy(struct ip_conntrack *ct) ++{ ++ struct ip_conntrack_expect *master = ct->master; ++ ++ DEBUGP(" entering\n"); ++ ++ if (!master) { ++ DEBUGP("no master exp for ct %p\n", ct); ++ return; ++ } ++ ++ ip_ct_gre_keymap_destroy(master); ++} ++ ++/* protocol helper struct */ ++static struct ip_conntrack_protocol gre = { { NULL, NULL }, IPPROTO_GRE, ++ "gre", ++ gre_pkt_to_tuple, ++ gre_invert_tuple, ++ gre_print_tuple, ++ gre_print_conntrack, ++ gre_packet, ++ gre_new, ++ gre_destroy, ++ NULL, ++ THIS_MODULE }; ++ ++/* ip_conntrack_proto_gre initialization */ ++static int __init init(void) ++{ ++ int retcode; ++ ++ if ((retcode = ip_conntrack_protocol_register(&gre))) { ++ printk(KERN_ERR "Unable to register conntrack protocol " ++ "helper for gre: %d\n", retcode); ++ return -EIO; ++ } ++ ++ return 0; ++} ++ ++static void __exit fini(void) ++{ ++ struct list_head *pos, *n; ++ ++ /* delete all keymap entries */ ++ WRITE_LOCK(&ip_ct_gre_lock); ++ list_for_each_safe(pos, n, &gre_keymap_list) { ++ DEBUGP("deleting keymap %p at module unload time\n", pos); ++ list_del(pos); ++ kfree(pos); ++ } ++ WRITE_UNLOCK(&ip_ct_gre_lock); ++ ++ ip_conntrack_protocol_unregister(&gre); ++} ++ ++EXPORT_SYMBOL(ip_ct_gre_keymap_add); ++EXPORT_SYMBOL(ip_ct_gre_keymap_change); ++EXPORT_SYMBOL(ip_ct_gre_keymap_destroy); ++ ++module_init(init); ++module_exit(fini); +diff -urN linux-2.4.29-old/net/ipv4/netfilter/ip_nat_core.c linux-2.4.29-dev/net/ipv4/netfilter/ip_nat_core.c +--- linux-2.4.29-old/net/ipv4/netfilter/ip_nat_core.c 2005-01-19 15:10:13.000000000 +0100 ++++ linux-2.4.29-dev/net/ipv4/netfilter/ip_nat_core.c 2005-04-02 19:07:18.000000000 +0200 +@@ -424,7 +424,7 @@ + *tuple = *orig_tuple; + while ((rptr = find_best_ips_proto_fast(tuple, mr, conntrack, hooknum)) + != NULL) { +- DEBUGP("Found best for "); DUMP_TUPLE(tuple); ++ DEBUGP("Found best for "); DUMP_TUPLE_RAW(tuple); + /* 3) The per-protocol part of the manip is made to + map into the range to make a unique tuple. */ + +@@ -564,9 +564,9 @@ + HOOK2MANIP(hooknum)==IP_NAT_MANIP_SRC ? "SRC" : "DST", + conntrack); + DEBUGP("Original: "); +- DUMP_TUPLE(&orig_tp); ++ DUMP_TUPLE_RAW(&orig_tp); + DEBUGP("New: "); +- DUMP_TUPLE(&new_tuple); ++ DUMP_TUPLE_RAW(&new_tuple); + #endif + + /* We now have two tuples (SRCIP/SRCPT/DSTIP/DSTPT): +diff -urN linux-2.4.29-old/net/ipv4/netfilter/ip_nat_pptp.c linux-2.4.29-dev/net/ipv4/netfilter/ip_nat_pptp.c +--- linux-2.4.29-old/net/ipv4/netfilter/ip_nat_pptp.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.4.29-dev/net/ipv4/netfilter/ip_nat_pptp.c 2005-04-02 19:14:52.000000000 +0200 +@@ -0,0 +1,475 @@ ++/* ++ * ip_nat_pptp.c - Version 1.5 ++ * ++ * NAT support for PPTP (Point to Point Tunneling Protocol). ++ * PPTP is a a protocol for creating virtual private networks. ++ * It is a specification defined by Microsoft and some vendors ++ * working with Microsoft. PPTP is built on top of a modified ++ * version of the Internet Generic Routing Encapsulation Protocol. ++ * GRE is defined in RFC 1701 and RFC 1702. Documentation of ++ * PPTP can be found in RFC 2637 ++ * ++ * (C) 2000-2003 by Harald Welte <laforge@gnumonks.org> ++ * ++ * Development of this code funded by Astaro AG (http://www.astaro.com/) ++ * ++ * TODO: - Support for multiple calls within one session ++ * (needs netfilter newnat code) ++ * - NAT to a unique tuple, not to TCP source port ++ * (needs netfilter tuple reservation) ++ * ++ * Changes: ++ * 2002-02-10 - Version 1.3 ++ * - Use ip_nat_mangle_tcp_packet() because of cloned skb's ++ * in local connections (Philip Craig <philipc@snapgear.com>) ++ * - add checks for magicCookie and pptp version ++ * - make argument list of pptp_{out,in}bound_packet() shorter ++ * - move to C99 style initializers ++ * - print version number at module loadtime ++ * 2003-09-22 - Version 1.5 ++ * - use SNATed tcp sourceport as callid, since we get called before ++ * TCP header is mangled (Philip Craig <philipc@snapgear.com>) ++ * ++ */ ++ ++#include <linux/config.h> ++#include <linux/module.h> ++#include <linux/ip.h> ++#include <linux/tcp.h> ++#include <net/tcp.h> ++#include <linux/netfilter_ipv4/ip_nat.h> ++#include <linux/netfilter_ipv4/ip_nat_rule.h> ++#include <linux/netfilter_ipv4/ip_nat_helper.h> ++#include <linux/netfilter_ipv4/ip_nat_pptp.h> ++#include <linux/netfilter_ipv4/ip_conntrack_helper.h> ++#include <linux/netfilter_ipv4/ip_conntrack_proto_gre.h> ++#include <linux/netfilter_ipv4/ip_conntrack_pptp.h> ++ ++#define IP_NAT_PPTP_VERSION "1.5" ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Harald Welte <laforge@gnumonks.org>"); ++MODULE_DESCRIPTION("Netfilter NAT helper module for PPTP"); ++ ++ ++#if 0 ++#include "ip_conntrack_pptp_priv.h" ++#define DEBUGP(format, args...) printk(KERN_DEBUG __FILE__ ":" __FUNCTION__ \ ++ ": " format, ## args) ++#else ++#define DEBUGP(format, args...) ++#endif ++ ++static unsigned int ++pptp_nat_expected(struct sk_buff **pskb, ++ unsigned int hooknum, ++ struct ip_conntrack *ct, ++ struct ip_nat_info *info) ++{ ++ struct ip_conntrack *master = master_ct(ct); ++ struct ip_nat_multi_range mr; ++ struct ip_ct_pptp_master *ct_pptp_info; ++ struct ip_nat_pptp *nat_pptp_info; ++ u_int32_t newip, newcid; ++ int ret; ++ ++ IP_NF_ASSERT(info); ++ IP_NF_ASSERT(master); ++ IP_NF_ASSERT(!(info->initialized & (1 << HOOK2MANIP(hooknum)))); ++ ++ DEBUGP("we have a connection!\n"); ++ ++ LOCK_BH(&ip_pptp_lock); ++ ct_pptp_info = &master->help.ct_pptp_info; ++ nat_pptp_info = &master->nat.help.nat_pptp_info; ++ ++ /* need to alter GRE tuple because conntrack expectfn() used 'wrong' ++ * (unmanipulated) values */ ++ if (HOOK2MANIP(hooknum) == IP_NAT_MANIP_DST) { ++ DEBUGP("completing tuples with NAT info \n"); ++ /* we can do this, since we're unconfirmed */ ++ if (ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.u.gre.key == ++ htonl(ct_pptp_info->pac_call_id)) { ++ /* assume PNS->PAC */ ++ ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.u.gre.key = ++ htonl(nat_pptp_info->pns_call_id); ++ ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.u.gre.key = ++ htonl(nat_pptp_info->pns_call_id); ++ newip = master->tuplehash[IP_CT_DIR_REPLY].tuple.src.ip; ++ newcid = htonl(nat_pptp_info->pac_call_id); ++ } else { ++ /* assume PAC->PNS */ ++ ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.u.gre.key = ++ htonl(nat_pptp_info->pac_call_id); ++ ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.u.gre.key = ++ htonl(nat_pptp_info->pac_call_id); ++ newip = master->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.ip; ++ newcid = htonl(nat_pptp_info->pns_call_id); ++ } ++ } else { ++ if (ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.u.gre.key == ++ htonl(ct_pptp_info->pac_call_id)) { ++ /* assume PNS->PAC */ ++ newip = master->tuplehash[IP_CT_DIR_REPLY].tuple.dst.ip; ++ newcid = htonl(ct_pptp_info->pns_call_id); ++ } ++ else { ++ /* assume PAC->PNS */ ++ newip = master->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.ip; ++ newcid = htonl(ct_pptp_info->pac_call_id); ++ } ++ } ++ ++ mr.rangesize = 1; ++ mr.range[0].flags = IP_NAT_RANGE_MAP_IPS | IP_NAT_RANGE_PROTO_SPECIFIED; ++ mr.range[0].min_ip = mr.range[0].max_ip = newip; ++ mr.range[0].min = mr.range[0].max = ++ ((union ip_conntrack_manip_proto ) { newcid }); ++ DEBUGP("change ip to %u.%u.%u.%u\n", ++ NIPQUAD(newip)); ++ DEBUGP("change key to 0x%x\n", ntohl(newcid)); ++ ret = ip_nat_setup_info(ct, &mr, hooknum); ++ ++ UNLOCK_BH(&ip_pptp_lock); ++ ++ return ret; ++ ++} ++ ++/* outbound packets == from PNS to PAC */ ++static inline unsigned int ++pptp_outbound_pkt(struct sk_buff **pskb, ++ struct ip_conntrack *ct, ++ enum ip_conntrack_info ctinfo, ++ struct ip_conntrack_expect *exp) ++ ++{ ++ struct iphdr *iph = (*pskb)->nh.iph; ++ struct tcphdr *tcph = (void *) iph + iph->ihl*4; ++ struct pptp_pkt_hdr *pptph = (struct pptp_pkt_hdr *) ++ ((void *)tcph + tcph->doff*4); ++ ++ struct PptpControlHeader *ctlh; ++ union pptp_ctrl_union pptpReq; ++ struct ip_ct_pptp_master *ct_pptp_info = &ct->help.ct_pptp_info; ++ struct ip_nat_pptp *nat_pptp_info = &ct->nat.help.nat_pptp_info; ++ ++ u_int16_t msg, *cid = NULL, new_callid; ++ ++ /* FIXME: size checks !!! */ ++ ctlh = (struct PptpControlHeader *) ((void *) pptph + sizeof(*pptph)); ++ pptpReq.rawreq = (void *) ((void *) ctlh + sizeof(*ctlh)); ++ ++ new_callid = htons(ct_pptp_info->pns_call_id); ++ ++ switch (msg = ntohs(ctlh->messageType)) { ++ case PPTP_OUT_CALL_REQUEST: ++ cid = &pptpReq.ocreq->callID; ++ /* FIXME: ideally we would want to reserve a call ID ++ * here. current netfilter NAT core is not able to do ++ * this :( For now we use TCP source port. This breaks ++ * multiple calls within one control session */ ++ ++ /* save original call ID in nat_info */ ++ nat_pptp_info->pns_call_id = ct_pptp_info->pns_call_id; ++ ++ /* don't use tcph->source since we are at a DSTmanip ++ * hook (e.g. PREROUTING) and pkt is not mangled yet */ ++ new_callid = ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.u.tcp.port; ++ ++ /* save new call ID in ct info */ ++ ct_pptp_info->pns_call_id = ntohs(new_callid); ++ break; ++ case PPTP_IN_CALL_REPLY: ++ cid = &pptpReq.icreq->callID; ++ break; ++ case PPTP_CALL_CLEAR_REQUEST: ++ cid = &pptpReq.clrreq->callID; ++ break; ++ default: ++ DEBUGP("unknown outbound packet 0x%04x:%s\n", msg, ++ (msg <= PPTP_MSG_MAX)? strMName[msg]:strMName[0]); ++ /* fall through */ ++ ++ case PPTP_SET_LINK_INFO: ++ /* only need to NAT in case PAC is behind NAT box */ ++ case PPTP_START_SESSION_REQUEST: ++ case PPTP_START_SESSION_REPLY: ++ case PPTP_STOP_SESSION_REQUEST: ++ case PPTP_STOP_SESSION_REPLY: ++ case PPTP_ECHO_REQUEST: ++ case PPTP_ECHO_REPLY: ++ /* no need to alter packet */ ++ return NF_ACCEPT; ++ } ++ ++ IP_NF_ASSERT(cid); ++ ++ DEBUGP("altering call id from 0x%04x to 0x%04x\n", ++ ntohs(*cid), ntohs(new_callid)); ++ ++ /* mangle packet */ ++ ip_nat_mangle_tcp_packet(pskb, ct, ctinfo, (void *)cid - (void *)pptph, ++ sizeof(new_callid), (char *)&new_callid, ++ sizeof(new_callid)); ++ ++ return NF_ACCEPT; ++} ++ ++/* inbound packets == from PAC to PNS */ ++static inline unsigned int ++pptp_inbound_pkt(struct sk_buff **pskb, ++ struct ip_conntrack *ct, ++ enum ip_conntrack_info ctinfo, ++ struct ip_conntrack_expect *oldexp) ++{ ++ struct iphdr *iph = (*pskb)->nh.iph; ++ struct tcphdr *tcph = (void *) iph + iph->ihl*4; ++ struct pptp_pkt_hdr *pptph = (struct pptp_pkt_hdr *) ++ ((void *)tcph + tcph->doff*4); ++ ++ struct PptpControlHeader *ctlh; ++ union pptp_ctrl_union pptpReq; ++ struct ip_ct_pptp_master *ct_pptp_info = &ct->help.ct_pptp_info; ++ struct ip_nat_pptp *nat_pptp_info = &ct->nat.help.nat_pptp_info; ++ ++ u_int16_t msg, new_cid = 0, new_pcid, *pcid = NULL, *cid = NULL; ++ u_int32_t old_dst_ip; ++ ++ struct ip_conntrack_tuple t, inv_t; ++ struct ip_conntrack_tuple *orig_t, *reply_t; ++ ++ /* FIXME: size checks !!! */ ++ ctlh = (struct PptpControlHeader *) ((void *) pptph + sizeof(*pptph)); ++ pptpReq.rawreq = (void *) ((void *) ctlh + sizeof(*ctlh)); ++ ++ new_pcid = htons(nat_pptp_info->pns_call_id); ++ ++ switch (msg = ntohs(ctlh->messageType)) { ++ case PPTP_OUT_CALL_REPLY: ++ pcid = &pptpReq.ocack->peersCallID; ++ cid = &pptpReq.ocack->callID; ++ if (!oldexp) { ++ DEBUGP("outcall but no expectation\n"); ++ break; ++ } ++ old_dst_ip = oldexp->tuple.dst.ip; ++ t = oldexp->tuple; ++ invert_tuplepr(&inv_t, &t); ++ ++ /* save original PAC call ID in nat_info */ ++ nat_pptp_info->pac_call_id = ct_pptp_info->pac_call_id; ++ ++ /* alter expectation */ ++ orig_t = &ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple; ++ reply_t = &ct->tuplehash[IP_CT_DIR_REPLY].tuple; ++ if (t.src.ip == orig_t->src.ip && t.dst.ip == orig_t->dst.ip) { ++ /* expectation for PNS->PAC direction */ ++ t.src.u.gre.key = htonl(nat_pptp_info->pns_call_id); ++ t.dst.u.gre.key = htonl(ct_pptp_info->pac_call_id); ++ inv_t.src.ip = reply_t->src.ip; ++ inv_t.dst.ip = reply_t->dst.ip; ++ inv_t.src.u.gre.key = htonl(nat_pptp_info->pac_call_id); ++ inv_t.dst.u.gre.key = htonl(ct_pptp_info->pns_call_id); ++ } else { ++ /* expectation for PAC->PNS direction */ ++ t.src.u.gre.key = htonl(nat_pptp_info->pac_call_id); ++ t.dst.u.gre.key = htonl(ct_pptp_info->pns_call_id); ++ inv_t.src.ip = orig_t->src.ip; ++ inv_t.dst.ip = orig_t->dst.ip; ++ inv_t.src.u.gre.key = htonl(nat_pptp_info->pns_call_id); ++ inv_t.dst.u.gre.key = htonl(ct_pptp_info->pac_call_id); ++ } ++ ++ if (!ip_conntrack_change_expect(oldexp, &t)) { ++ DEBUGP("successfully changed expect\n"); ++ } else { ++ DEBUGP("can't change expect\n"); ++ } ++ ip_ct_gre_keymap_change(oldexp->proto.gre.keymap_orig, &t); ++ ip_ct_gre_keymap_change(oldexp->proto.gre.keymap_reply, &inv_t); ++ break; ++ case PPTP_IN_CALL_CONNECT: ++ pcid = &pptpReq.iccon->peersCallID; ++ if (!oldexp) ++ break; ++ old_dst_ip = oldexp->tuple.dst.ip; ++ t = oldexp->tuple; ++ ++ /* alter expectation, no need for callID */ ++ if (t.dst.ip == ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.ip) { ++ /* expectation for PNS->PAC direction */ ++ t.src.ip = ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.ip; ++ } else { ++ /* expectation for PAC->PNS direction */ ++ t.dst.ip = ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.ip; ++ } ++ ++ if (!ip_conntrack_change_expect(oldexp, &t)) { ++ DEBUGP("successfully changed expect\n"); ++ } else { ++ DEBUGP("can't change expect\n"); ++ } ++ break; ++ case PPTP_IN_CALL_REQUEST: ++ /* only need to nat in case PAC is behind NAT box */ ++ break; ++ case PPTP_WAN_ERROR_NOTIFY: ++ pcid = &pptpReq.wanerr->peersCallID; ++ break; ++ case PPTP_CALL_DISCONNECT_NOTIFY: ++ pcid = &pptpReq.disc->callID; ++ break; ++ ++ default: ++ DEBUGP("unknown inbound packet %s\n", ++ (msg <= PPTP_MSG_MAX)? strMName[msg]:strMName[0]); ++ /* fall through */ ++ ++ case PPTP_START_SESSION_REQUEST: ++ case PPTP_START_SESSION_REPLY: ++ case PPTP_STOP_SESSION_REQUEST: ++ case PPTP_STOP_SESSION_REPLY: ++ case PPTP_ECHO_REQUEST: ++ case PPTP_ECHO_REPLY: ++ /* no need to alter packet */ ++ return NF_ACCEPT; ++ } ++ ++ /* mangle packet */ ++ IP_NF_ASSERT(pcid); ++ DEBUGP("altering peer call id from 0x%04x to 0x%04x\n", ++ ntohs(*pcid), ntohs(new_pcid)); ++ ip_nat_mangle_tcp_packet(pskb, ct, ctinfo, (void *)pcid - (void *)pptph, ++ sizeof(new_pcid), (char *)&new_pcid, ++ sizeof(new_pcid)); ++ ++ if (new_cid) { ++ IP_NF_ASSERT(cid); ++ DEBUGP("altering call id from 0x%04x to 0x%04x\n", ++ ntohs(*cid), ntohs(new_cid)); ++ ip_nat_mangle_tcp_packet(pskb, ct, ctinfo, ++ (void *)cid - (void *)pptph, ++ sizeof(new_cid), (char *)&new_cid, ++ sizeof(new_cid)); ++ } ++ ++ /* great, at least we don't need to resize packets */ ++ return NF_ACCEPT; ++} ++ ++ ++static unsigned int tcp_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 = (*pskb)->len - iph->ihl*4 - tcph->doff*4; ++ struct pptp_pkt_hdr *pptph; ++ ++ int dir; ++ ++ DEBUGP("entering\n"); ++ ++ /* Only mangle things once: DST for original direction ++ and SRC for reply direction. */ ++ dir = CTINFO2DIR(ctinfo); ++ if (!((HOOK2MANIP(hooknum) == IP_NAT_MANIP_SRC ++ && dir == IP_CT_DIR_ORIGINAL) ++ || (HOOK2MANIP(hooknum) == IP_NAT_MANIP_DST ++ && 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" ++ : hooknum == NF_IP_LOCAL_IN ? "INPUT" : "???"); ++ return NF_ACCEPT; ++ } ++ ++ /* if packet is too small, just skip it */ ++ if (datalen < sizeof(struct pptp_pkt_hdr)+ ++ sizeof(struct PptpControlHeader)) { ++ DEBUGP("pptp packet too short\n"); ++ return NF_ACCEPT; ++ } ++ ++ pptph = (struct pptp_pkt_hdr *) ((void *)tcph + tcph->doff*4); ++ ++ /* if it's not a control message, we can't handle it */ ++ if (ntohs(pptph->packetType) != PPTP_PACKET_CONTROL || ++ ntohl(pptph->magicCookie) != PPTP_MAGIC_COOKIE) { ++ DEBUGP("not a pptp control packet\n"); ++ return NF_ACCEPT; ++ } ++ ++ LOCK_BH(&ip_pptp_lock); ++ ++ if (dir == IP_CT_DIR_ORIGINAL) { ++ /* reuqests sent by client to server (PNS->PAC) */ ++ pptp_outbound_pkt(pskb, ct, ctinfo, exp); ++ } else { ++ /* response from the server to the client (PAC->PNS) */ ++ pptp_inbound_pkt(pskb, ct, ctinfo, exp); ++ } ++ ++ UNLOCK_BH(&ip_pptp_lock); ++ ++ return NF_ACCEPT; ++} ++ ++/* nat helper struct for control connection */ ++static struct ip_nat_helper pptp_tcp_helper = { ++ .list = { NULL, NULL }, ++ .name = "pptp", ++ .flags = IP_NAT_HELPER_F_ALWAYS, ++ .me = THIS_MODULE, ++ .tuple = { .src = { .ip = 0, ++ .u = { .tcp = { .port = ++ __constant_htons(PPTP_CONTROL_PORT) } ++ } ++ }, ++ .dst = { .ip = 0, ++ .u = { .all = 0 }, ++ .protonum = IPPROTO_TCP ++ } ++ }, ++ ++ .mask = { .src = { .ip = 0, ++ .u = { .tcp = { .port = 0xFFFF } } ++ }, ++ .dst = { .ip = 0, ++ .u = { .all = 0 }, ++ .protonum = 0xFFFF ++ } ++ }, ++ .help = tcp_help, ++ .expect = pptp_nat_expected ++}; ++ ++ ++static int __init init(void) ++{ ++ DEBUGP("%s: registering NAT helper\n", __FILE__); ++ if (ip_nat_helper_register(&pptp_tcp_helper)) { ++ printk(KERN_ERR "Unable to register NAT application helper " ++ "for pptp\n"); ++ return -EIO; ++ } ++ ++ printk("ip_nat_pptp version %s loaded\n", IP_NAT_PPTP_VERSION); ++ return 0; ++} ++ ++static void __exit fini(void) ++{ ++ DEBUGP("cleanup_module\n" ); ++ ip_nat_helper_unregister(&pptp_tcp_helper); ++ printk("ip_nat_pptp version %s unloaded\n", IP_NAT_PPTP_VERSION); ++} ++ ++module_init(init); ++module_exit(fini); +Binary files linux-2.4.29-old/net/ipv4/netfilter/.ip_nat_pptp.c.swp and linux-2.4.29-dev/net/ipv4/netfilter/.ip_nat_pptp.c.swp differ +diff -urN linux-2.4.29-old/net/ipv4/netfilter/ip_nat_proto_gre.c linux-2.4.29-dev/net/ipv4/netfilter/ip_nat_proto_gre.c +--- linux-2.4.29-old/net/ipv4/netfilter/ip_nat_proto_gre.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.4.29-dev/net/ipv4/netfilter/ip_nat_proto_gre.c 2005-04-02 19:15:10.000000000 +0200 +@@ -0,0 +1,202 @@ ++/* ++ * ip_nat_proto_gre.c - Version 1.2 ++ * ++ * NAT protocol helper module for GRE. ++ * ++ * GRE is a generic encapsulation protocol, which is generally not very ++ * suited for NAT, as it has no protocol-specific part as port numbers. ++ * ++ * It has an optional key field, which may help us distinguishing two ++ * connections between the same two hosts. ++ * ++ * GRE is defined in RFC 1701 and RFC 1702, as well as RFC 2784 ++ * ++ * PPTP is built on top of a modified version of GRE, and has a mandatory ++ * field called "CallID", which serves us for the same purpose as the key ++ * field in plain GRE. ++ * ++ * Documentation about PPTP can be found in RFC 2637 ++ * ++ * (C) 2000-2003 by Harald Welte <laforge@gnumonks.org> ++ * ++ * Development of this code funded by Astaro AG (http://www.astaro.com/) ++ * ++ */ ++ ++#include <linux/config.h> ++#include <linux/module.h> ++#include <linux/ip.h> ++#include <linux/netfilter_ipv4/ip_nat.h> ++#include <linux/netfilter_ipv4/ip_nat_rule.h> ++#include <linux/netfilter_ipv4/ip_nat_protocol.h> ++#include <linux/netfilter_ipv4/ip_conntrack_proto_gre.h> ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Harald Welte <laforge@gnumonks.org>"); ++MODULE_DESCRIPTION("Netfilter NAT protocol helper module for GRE"); ++ ++#if 0 ++#define DEBUGP(format, args...) printk(KERN_DEBUG __FILE__ ":" __FUNCTION__ \ ++ ": " format, ## args) ++#else ++#define DEBUGP(x, args...) ++#endif ++ ++/* is key in given range between min and max */ ++static int ++gre_in_range(const struct ip_conntrack_tuple *tuple, ++ enum ip_nat_manip_type maniptype, ++ const union ip_conntrack_manip_proto *min, ++ const union ip_conntrack_manip_proto *max) ++{ ++ u_int32_t key; ++ ++ if (maniptype == IP_NAT_MANIP_SRC) ++ key = tuple->src.u.gre.key; ++ else ++ key = tuple->dst.u.gre.key; ++ ++ return ntohl(key) >= ntohl(min->gre.key) ++ && ntohl(key) <= ntohl(max->gre.key); ++} ++ ++/* generate unique tuple ... */ ++static int ++gre_unique_tuple(struct ip_conntrack_tuple *tuple, ++ const struct ip_nat_range *range, ++ enum ip_nat_manip_type maniptype, ++ const struct ip_conntrack *conntrack) ++{ ++ u_int32_t min, i, range_size; ++ u_int32_t key = 0, *keyptr; ++ ++ if (maniptype == IP_NAT_MANIP_SRC) ++ keyptr = &tuple->src.u.gre.key; ++ else ++ keyptr = &tuple->dst.u.gre.key; ++ ++ if (!(range->flags & IP_NAT_RANGE_PROTO_SPECIFIED)) { ++ ++ DEBUGP("%p: NATing GRE PPTP\n", conntrack); ++ min = 1; ++ range_size = 0xffff; ++ ++ } else { ++ min = ntohl(range->min.gre.key); ++ range_size = ntohl(range->max.gre.key) - min + 1; ++ } ++ ++ DEBUGP("min = %u, range_size = %u\n", min, range_size); ++ ++ for (i = 0; i < range_size; i++, key++) { ++ *keyptr = htonl(min + key % range_size); ++ if (!ip_nat_used_tuple(tuple, conntrack)) ++ return 1; ++ } ++ ++ DEBUGP("%p: no NAT mapping\n", conntrack); ++ ++ return 0; ++} ++ ++/* manipulate a GRE packet according to maniptype */ ++static void ++gre_manip_pkt(struct iphdr *iph, size_t len, ++ const struct ip_conntrack_manip *manip, ++ enum ip_nat_manip_type maniptype) ++{ ++ struct gre_hdr *greh = (struct gre_hdr *)((u_int32_t *)iph+iph->ihl); ++ struct gre_hdr_pptp *pgreh = (struct gre_hdr_pptp *) greh; ++ ++ /* we only have destination manip of a packet, since 'source key' ++ * is not present in the packet itself */ ++ if (maniptype == IP_NAT_MANIP_DST) { ++ /* key manipulation is always dest */ ++ switch (greh->version) { ++ case 0: ++ if (!greh->key) { ++ DEBUGP("can't nat GRE w/o key\n"); ++ break; ++ } ++ if (greh->csum) { ++ /* FIXME: Never tested this code... */ ++ *(gre_csum(greh)) = ++ ip_nat_cheat_check(~*(gre_key(greh)), ++ manip->u.gre.key, ++ *(gre_csum(greh))); ++ } ++ *(gre_key(greh)) = manip->u.gre.key; ++ break; ++ case GRE_VERSION_PPTP: ++ DEBUGP("call_id -> 0x%04x\n", ++ ntohl(manip->u.gre.key)); ++ pgreh->call_id = htons(ntohl(manip->u.gre.key)); ++ break; ++ default: ++ DEBUGP("can't nat unknown GRE version\n"); ++ break; ++ } ++ } ++} ++ ++/* print out a nat tuple */ ++static unsigned int ++gre_print(char *buffer, ++ const struct ip_conntrack_tuple *match, ++ const struct ip_conntrack_tuple *mask) ++{ ++ unsigned int len = 0; ++ ++ if (mask->src.u.gre.key) ++ len += sprintf(buffer + len, "srckey=0x%x ", ++ ntohl(match->src.u.gre.key)); ++ ++ if (mask->dst.u.gre.key) ++ len += sprintf(buffer + len, "dstkey=0x%x ", ++ ntohl(match->src.u.gre.key)); ++ ++ return len; ++} ++ ++/* print a range of keys */ ++static unsigned int ++gre_print_range(char *buffer, const struct ip_nat_range *range) ++{ ++ if (range->min.gre.key != 0 ++ || range->max.gre.key != 0xFFFF) { ++ if (range->min.gre.key == range->max.gre.key) ++ return sprintf(buffer, "key 0x%x ", ++ ntohl(range->min.gre.key)); ++ else ++ return sprintf(buffer, "keys 0x%u-0x%u ", ++ ntohl(range->min.gre.key), ++ ntohl(range->max.gre.key)); ++ } else ++ return 0; ++} ++ ++/* nat helper struct */ ++static struct ip_nat_protocol gre = ++ { { NULL, NULL }, "GRE", IPPROTO_GRE, ++ gre_manip_pkt, ++ gre_in_range, ++ gre_unique_tuple, ++ gre_print, ++ gre_print_range ++ }; ++ ++static int __init init(void) ++{ ++ if (ip_nat_protocol_register(&gre)) ++ return -EIO; ++ ++ return 0; ++} ++ ++static void __exit fini(void) ++{ ++ ip_nat_protocol_unregister(&gre); ++} ++ ++module_init(init); ++module_exit(fini); +diff -urN linux-2.4.29-old/net/ipv4/netfilter/Makefile linux-2.4.29-dev/net/ipv4/netfilter/Makefile +--- linux-2.4.29-old/net/ipv4/netfilter/Makefile 2005-04-02 19:04:11.000000000 +0200 ++++ linux-2.4.29-dev/net/ipv4/netfilter/Makefile 2005-04-02 19:07:18.000000000 +0200 +@@ -36,23 +36,32 @@ + ifdef CONFIG_IP_NF_AMANDA + export-objs += ip_conntrack_amanda.o + endif +- + obj-$(CONFIG_IP_NF_TFTP) += ip_conntrack_tftp.o + obj-$(CONFIG_IP_NF_FTP) += ip_conntrack_ftp.o + ifdef CONFIG_IP_NF_FTP + export-objs += ip_conntrack_ftp.o + endif +- + obj-$(CONFIG_IP_NF_IRC) += ip_conntrack_irc.o + ifdef CONFIG_IP_NF_IRC + export-objs += ip_conntrack_irc.o + endif ++obj-$(CONFIG_IP_NF_CT_PROTO_GRE) += ip_conntrack_proto_gre.o ++ifdef CONFIG_IP_NF_CT_PROTO_GRE ++ export-objs += ip_conntrack_proto_gre.o ++endif ++obj-$(CONFIG_IP_NF_PPTP) += ip_conntrack_pptp.o ++ifdef CONFIG_IP_NF_NAT_PPTP ++ export-objs += ip_conntrack_pptp.o ++endif ++ + + # NAT helpers + 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_PROTO_GRE) += ip_nat_proto_gre.o ++obj-$(CONFIG_IP_NF_NAT_PPTP) += ip_nat_pptp.o + + # generic IP tables + obj-$(CONFIG_IP_NF_IPTABLES) += ip_tables.o diff --git a/openwrt/target/linux/linux-2.4/patches/generic/604-netfilter_maxconn.patch b/openwrt/target/linux/linux-2.4/patches/generic/604-netfilter_maxconn.patch new file mode 100644 index 000000000..001ed67b8 --- /dev/null +++ b/openwrt/target/linux/linux-2.4/patches/generic/604-netfilter_maxconn.patch @@ -0,0 +1,20 @@ +--- ../../buildroot-unpacked/build_mipsel/linux/net/ipv4/netfilter/ip_conntrack_core.c 2004-11-28 22:59:36.000000000 -0500 ++++ linux/net/ipv4/netfilter/ip_conntrack_core.c 2004-11-30 05:05:32.000000000 -0500 +@@ -1386,7 +1386,7 @@ + nf_unregister_sockopt(&so_getorigdst); + } + +-static int hashsize = 0; ++static int hashsize = 5953; + MODULE_PARM(hashsize, "i"); + + int __init ip_conntrack_init(void) +@@ -1407,7 +1407,7 @@ + if (ip_conntrack_htable_size < 16) + ip_conntrack_htable_size = 16; + } +- ip_conntrack_max = 8 * ip_conntrack_htable_size; ++ ip_conntrack_max = ip_conntrack_htable_size; + + printk("ip_conntrack version %s (%u buckets, %d max)" + " - %Zd bytes per conntrack\n", IP_CONNTRACK_VERSION, diff --git a/openwrt/target/linux/linux-2.4/patches/generic/605-netfilter_TTL.patch b/openwrt/target/linux/linux-2.4/patches/generic/605-netfilter_TTL.patch new file mode 100644 index 000000000..0aa3c8043 --- /dev/null +++ b/openwrt/target/linux/linux-2.4/patches/generic/605-netfilter_TTL.patch @@ -0,0 +1,180 @@ +diff -urN linux-2.4.30.old/Documentation/Configure.help linux-2.4.30.dev/Documentation/Configure.help +--- linux-2.4.30.old/Documentation/Configure.help 2005-04-27 11:35:46.000000000 +0200 ++++ linux-2.4.30.dev/Documentation/Configure.help 2005-04-27 11:43:49.000000000 +0200 +@@ -3209,6 +3209,15 @@ + If you want to compile it as a module, say M here and read + <file:Documentation/modules.txt>. If unsure, say `N'. + ++TTL target support ++CONFIG_IP_NF_TARGET_TTL ++ This option adds a `TTL' target, which enables the user to set ++ the TTL value or increment / decrement the TTL value by a given ++ amount. ++ ++ If you want to compile it as a module, say M here and read ++ Documentation/modules.txt. If unsure, say `N'. ++ + ipchains (2.2-style) support + CONFIG_IP_NF_COMPAT_IPCHAINS + This option places ipchains (with masquerading and redirection +diff -urN linux-2.4.30.old/include/linux/netfilter_ipv4/ipt_TTL.h linux-2.4.30.dev/include/linux/netfilter_ipv4/ipt_TTL.h +--- linux-2.4.30.old/include/linux/netfilter_ipv4/ipt_TTL.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.4.30.dev/include/linux/netfilter_ipv4/ipt_TTL.h 2005-04-27 11:43:49.000000000 +0200 +@@ -0,0 +1,21 @@ ++/* TTL modification module for IP tables ++ * (C) 2000 by Harald Welte <laforge@gnumonks.org> */ ++ ++#ifndef _IPT_TTL_H ++#define _IPT_TTL_H ++ ++enum { ++ IPT_TTL_SET = 0, ++ IPT_TTL_INC, ++ IPT_TTL_DEC ++}; ++ ++#define IPT_TTL_MAXMODE IPT_TTL_DEC ++ ++struct ipt_TTL_info { ++ u_int8_t mode; ++ u_int8_t ttl; ++}; ++ ++ ++#endif +diff -urN linux-2.4.30.old/net/ipv4/netfilter/Config.in linux-2.4.30.dev/net/ipv4/netfilter/Config.in +--- linux-2.4.30.old/net/ipv4/netfilter/Config.in 2005-04-27 11:35:45.000000000 +0200 ++++ linux-2.4.30.dev/net/ipv4/netfilter/Config.in 2005-04-27 11:43:49.000000000 +0200 +@@ -129,6 +129,7 @@ + dep_tristate ' MARK target support' CONFIG_IP_NF_TARGET_MARK $CONFIG_IP_NF_MANGLE + fi + dep_tristate ' LOG target support' CONFIG_IP_NF_TARGET_LOG $CONFIG_IP_NF_IPTABLES ++ dep_tristate ' TTL target support' CONFIG_IP_NF_TARGET_TTL $CONFIG_IP_NF_IPTABLES + dep_tristate ' ULOG target support' CONFIG_IP_NF_TARGET_ULOG $CONFIG_IP_NF_IPTABLES + dep_tristate ' TCPMSS target support' CONFIG_IP_NF_TARGET_TCPMSS $CONFIG_IP_NF_IPTABLES + fi +diff -urN linux-2.4.30.old/net/ipv4/netfilter/ipt_TTL.c linux-2.4.30.dev/net/ipv4/netfilter/ipt_TTL.c +--- linux-2.4.30.old/net/ipv4/netfilter/ipt_TTL.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.4.30.dev/net/ipv4/netfilter/ipt_TTL.c 2005-04-27 11:43:49.000000000 +0200 +@@ -0,0 +1,110 @@ ++/* TTL modification target for IP tables ++ * (C) 2000 by Harald Welte <laforge@gnumonks.org> ++ * ++ * Version: $Revision: 1.1 $ ++ * ++ * This software is distributed under the terms of GNU GPL ++ */ ++ ++#include <linux/module.h> ++#include <linux/skbuff.h> ++#include <linux/ip.h> ++#include <net/checksum.h> ++ ++#include <linux/netfilter_ipv4/ip_tables.h> ++#include <linux/netfilter_ipv4/ipt_TTL.h> ++ ++MODULE_AUTHOR("Harald Welte <laforge@gnumonks.org>"); ++MODULE_DESCRIPTION("IP tables TTL modification module"); ++MODULE_LICENSE("GPL"); ++ ++static unsigned int ipt_ttl_target(struct sk_buff **pskb, unsigned int hooknum, ++ const struct net_device *in, const struct net_device *out, ++ const void *targinfo, void *userinfo) ++{ ++ struct iphdr *iph = (*pskb)->nh.iph; ++ const struct ipt_TTL_info *info = targinfo; ++ u_int16_t diffs[2]; ++ int new_ttl; ++ ++ switch (info->mode) { ++ case IPT_TTL_SET: ++ new_ttl = info->ttl; ++ break; ++ case IPT_TTL_INC: ++ new_ttl = iph->ttl + info->ttl; ++ if (new_ttl > 255) ++ new_ttl = 255; ++ break; ++ case IPT_TTL_DEC: ++ new_ttl = iph->ttl - info->ttl; ++ if (new_ttl < 0) ++ new_ttl = 0; ++ break; ++ default: ++ new_ttl = iph->ttl; ++ break; ++ } ++ ++ if (new_ttl != iph->ttl) { ++ diffs[0] = htons(((unsigned)iph->ttl) << 8) ^ 0xFFFF; ++ iph->ttl = new_ttl; ++ diffs[1] = htons(((unsigned)iph->ttl) << 8); ++ iph->check = csum_fold(csum_partial((char *)diffs, ++ sizeof(diffs), ++ iph->check^0xFFFF)); ++ (*pskb)->nfcache |= NFC_ALTERED; ++ } ++ ++ return IPT_CONTINUE; ++} ++ ++static int ipt_ttl_checkentry(const char *tablename, ++ const struct ipt_entry *e, ++ void *targinfo, ++ unsigned int targinfosize, ++ unsigned int hook_mask) ++{ ++ struct ipt_TTL_info *info = targinfo; ++ ++ if (targinfosize != IPT_ALIGN(sizeof(struct ipt_TTL_info))) { ++ printk(KERN_WARNING "TTL: targinfosize %u != %Zu\n", ++ targinfosize, ++ IPT_ALIGN(sizeof(struct ipt_TTL_info))); ++ return 0; ++ } ++ ++ if (strcmp(tablename, "mangle")) { ++ printk(KERN_WARNING "TTL: can only be called from \"mangle\" table, not \"%s\"\n", tablename); ++ return 0; ++ } ++ ++ if (info->mode > IPT_TTL_MAXMODE) { ++ printk(KERN_WARNING "TTL: invalid or unknown Mode %u\n", ++ info->mode); ++ return 0; ++ } ++ ++ if ((info->mode != IPT_TTL_SET) && (info->ttl == 0)) { ++ printk(KERN_WARNING "TTL: increment/decrement doesn't make sense with value 0\n"); ++ return 0; ++ } ++ ++ return 1; ++} ++ ++static struct ipt_target ipt_TTL = { { NULL, NULL }, "TTL", ++ ipt_ttl_target, ipt_ttl_checkentry, NULL, THIS_MODULE }; ++ ++static int __init init(void) ++{ ++ return ipt_register_target(&ipt_TTL); ++} ++ ++static void __exit fini(void) ++{ ++ ipt_unregister_target(&ipt_TTL); ++} ++ ++module_init(init); ++module_exit(fini); +diff -urN linux-2.4.30.old/net/ipv4/netfilter/Makefile linux-2.4.30.dev/net/ipv4/netfilter/Makefile +--- linux-2.4.30.old/net/ipv4/netfilter/Makefile 2005-04-27 11:35:45.000000000 +0200 ++++ linux-2.4.30.dev/net/ipv4/netfilter/Makefile 2005-04-27 11:43:49.000000000 +0200 +@@ -112,6 +112,7 @@ + obj-$(CONFIG_IP_NF_TARGET_REDIRECT) += ipt_REDIRECT.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_TTL) += ipt_TTL.o + obj-$(CONFIG_IP_NF_TARGET_ULOG) += ipt_ULOG.o + obj-$(CONFIG_IP_NF_TARGET_TCPMSS) += ipt_TCPMSS.o + diff --git a/openwrt/target/linux/linux-2.4/patches/generic/606-netfilter_NETMAP.patch b/openwrt/target/linux/linux-2.4/patches/generic/606-netfilter_NETMAP.patch new file mode 100644 index 000000000..ccd639181 --- /dev/null +++ b/openwrt/target/linux/linux-2.4/patches/generic/606-netfilter_NETMAP.patch @@ -0,0 +1,159 @@ +diff -urN linux-2.4.30.orig/Documentation/Configure.help linux-2.4.30/Documentation/Configure.help +--- linux-2.4.30.orig/Documentation/Configure.help 2005-07-01 02:06:36.000000000 +0200 ++++ linux-2.4.30/Documentation/Configure.help 2005-07-01 00:41:09.000000000 +0200 +@@ -3086,6 +3086,17 @@ + If you want to compile it as a module, say M here and read + <file:Documentation/modules.txt>. If unsure, say `N'. + ++NETMAP target support ++CONFIG_IP_NF_TARGET_NETMAP ++ NETMAP is an implementation of static 1:1 NAT mapping of network ++ addresses. It maps the network address part, while keeping the ++ host address part intact. It is similar to Fast NAT, except that ++ Netfilter's connection tracking doesn't work well with Fast NAT. ++ ++ If you want to compile it as a module, say M here and read ++ Documentation/modules.txt. The module will be called ++ ipt_NETMAP.o. If unsure, say `N'. ++ + Packet mangling + CONFIG_IP_NF_MANGLE + This option adds a `mangle' table to iptables: see the man page for +diff -urN linux-2.4.30.orig/net/ipv4/netfilter/Config.in linux-2.4.30/net/ipv4/netfilter/Config.in +--- linux-2.4.30.orig/net/ipv4/netfilter/Config.in 2005-07-01 02:06:35.000000000 +0200 ++++ linux-2.4.30/net/ipv4/netfilter/Config.in 2005-07-01 00:41:09.000000000 +0200 +@@ -69,6 +69,7 @@ + define_bool CONFIG_IP_NF_NAT_NEEDED y + dep_tristate ' MASQUERADE target support' CONFIG_IP_NF_TARGET_MASQUERADE $CONFIG_IP_NF_NAT + dep_tristate ' REDIRECT target support' CONFIG_IP_NF_TARGET_REDIRECT $CONFIG_IP_NF_NAT ++ dep_tristate ' NETMAP target support' CONFIG_IP_NF_TARGET_NETMAP $CONFIG_IP_NF_NAT + if [ "$CONFIG_IP_NF_PPTP" = "m" ]; then + define_tristate CONFIG_IP_NF_NAT_PPTP m + else +diff -urN linux-2.4.30.orig/net/ipv4/netfilter/ipt_NETMAP.c linux-2.4.30/net/ipv4/netfilter/ipt_NETMAP.c +--- linux-2.4.30.orig/net/ipv4/netfilter/ipt_NETMAP.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.4.30/net/ipv4/netfilter/ipt_NETMAP.c 2005-07-01 00:41:09.000000000 +0200 +@@ -0,0 +1,112 @@ ++/* NETMAP - static NAT mapping of IP network addresses (1:1). ++ The mapping can be applied to source (POSTROUTING), ++ destination (PREROUTING), or both (with separate rules). ++ ++ Author: Svenning Soerensen <svenning@post5.tele.dk> ++*/ ++ ++#include <linux/config.h> ++#include <linux/ip.h> ++#include <linux/module.h> ++#include <linux/netdevice.h> ++#include <linux/netfilter.h> ++#include <linux/netfilter_ipv4.h> ++#include <linux/netfilter_ipv4/ip_nat_rule.h> ++ ++#define MODULENAME "NETMAP" ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Svenning Soerensen <svenning@post5.tele.dk>"); ++MODULE_DESCRIPTION("iptables 1:1 NAT mapping of IP networks target"); ++ ++#if 0 ++#define DEBUGP printk ++#else ++#define DEBUGP(format, args...) ++#endif ++ ++static int ++check(const char *tablename, ++ const struct ipt_entry *e, ++ void *targinfo, ++ unsigned int targinfosize, ++ unsigned int hook_mask) ++{ ++ const struct ip_nat_multi_range *mr = targinfo; ++ ++ if (strcmp(tablename, "nat") != 0) { ++ DEBUGP(MODULENAME":check: bad table `%s'.\n", tablename); ++ return 0; ++ } ++ if (targinfosize != IPT_ALIGN(sizeof(*mr))) { ++ DEBUGP(MODULENAME":check: size %u.\n", targinfosize); ++ return 0; ++ } ++ if (hook_mask & ~((1 << NF_IP_PRE_ROUTING) | (1 << NF_IP_POST_ROUTING))) { ++ DEBUGP(MODULENAME":check: bad hooks %x.\n", hook_mask); ++ return 0; ++ } ++ if (!(mr->range[0].flags & IP_NAT_RANGE_MAP_IPS)) { ++ DEBUGP(MODULENAME":check: bad MAP_IPS.\n"); ++ return 0; ++ } ++ if (mr->rangesize != 1) { ++ DEBUGP(MODULENAME":check: bad rangesize %u.\n", mr->rangesize); ++ return 0; ++ } ++ return 1; ++} ++ ++static unsigned int ++target(struct sk_buff **pskb, ++ unsigned int hooknum, ++ const struct net_device *in, ++ const struct net_device *out, ++ const void *targinfo, ++ void *userinfo) ++{ ++ struct ip_conntrack *ct; ++ enum ip_conntrack_info ctinfo; ++ u_int32_t new_ip, netmask; ++ const struct ip_nat_multi_range *mr = targinfo; ++ struct ip_nat_multi_range newrange; ++ ++ IP_NF_ASSERT(hooknum == NF_IP_PRE_ROUTING ++ || hooknum == NF_IP_POST_ROUTING); ++ ct = ip_conntrack_get(*pskb, &ctinfo); ++ ++ netmask = ~(mr->range[0].min_ip ^ mr->range[0].max_ip); ++ ++ if (hooknum == NF_IP_PRE_ROUTING) ++ new_ip = (*pskb)->nh.iph->daddr & ~netmask; ++ else ++ new_ip = (*pskb)->nh.iph->saddr & ~netmask; ++ new_ip |= mr->range[0].min_ip & netmask; ++ ++ newrange = ((struct ip_nat_multi_range) ++ { 1, { { mr->range[0].flags | IP_NAT_RANGE_MAP_IPS, ++ new_ip, new_ip, ++ mr->range[0].min, mr->range[0].max } } }); ++ ++ /* Hand modified range to generic setup. */ ++ return ip_nat_setup_info(ct, &newrange, hooknum); ++} ++ ++static struct ipt_target target_module = { ++ .name = MODULENAME, ++ .target = target, ++ .checkentry = check, ++ .me = THIS_MODULE ++}; ++ ++static int __init init(void) ++{ ++ return ipt_register_target(&target_module); ++} ++ ++static void __exit fini(void) ++{ ++ ipt_unregister_target(&target_module); ++} ++ ++module_init(init); ++module_exit(fini); +diff -urN linux-2.4.30.orig/net/ipv4/netfilter/Makefile linux-2.4.30/net/ipv4/netfilter/Makefile +--- linux-2.4.30.orig/net/ipv4/netfilter/Makefile 2005-07-01 02:06:35.000000000 +0200 ++++ linux-2.4.30/net/ipv4/netfilter/Makefile 2005-07-01 00:41:09.000000000 +0200 +@@ -110,6 +110,7 @@ + obj-$(CONFIG_IP_NF_TARGET_MARK) += ipt_MARK.o + obj-$(CONFIG_IP_NF_TARGET_MASQUERADE) += ipt_MASQUERADE.o + obj-$(CONFIG_IP_NF_TARGET_REDIRECT) += ipt_REDIRECT.o ++obj-$(CONFIG_IP_NF_TARGET_NETMAP) += ipt_NETMAP.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_TTL) += ipt_TTL.o diff --git a/openwrt/target/linux/linux-2.4/patches/generic/607-netfilter_connmark.patch b/openwrt/target/linux/linux-2.4/patches/generic/607-netfilter_connmark.patch new file mode 100644 index 000000000..154314dc4 --- /dev/null +++ b/openwrt/target/linux/linux-2.4/patches/generic/607-netfilter_connmark.patch @@ -0,0 +1,351 @@ +diff -urN linux.old/include/linux/netfilter_ipv4/ip_conntrack.h linux.dev/include/linux/netfilter_ipv4/ip_conntrack.h +--- linux.old/include/linux/netfilter_ipv4/ip_conntrack.h 2005-08-20 20:02:06.619827000 +0200 ++++ linux.dev/include/linux/netfilter_ipv4/ip_conntrack.h 2005-08-20 20:19:23.302029232 +0200 +@@ -226,6 +226,9 @@ + unsigned int app_data_len; + } layer7; + #endif ++#if defined(CONFIG_IP_NF_CONNTRACK_MARK) ++ unsigned long mark; ++#endif + }; + + /* get master conntrack via master expectation */ +diff -urN linux.old/include/linux/netfilter_ipv4/ipt_CONNMARK.h linux.dev/include/linux/netfilter_ipv4/ipt_CONNMARK.h +--- linux.old/include/linux/netfilter_ipv4/ipt_CONNMARK.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux.dev/include/linux/netfilter_ipv4/ipt_CONNMARK.h 2005-08-20 20:19:41.058329864 +0200 +@@ -0,0 +1,25 @@ ++#ifndef _IPT_CONNMARK_H_target ++#define _IPT_CONNMARK_H_target ++ ++/* Copyright (C) 2002,2004 MARA Systems AB <http://www.marasystems.com> ++ * by Henrik Nordstrom <hno@marasystems.com> ++ * ++ * 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 -urN linux.old/include/linux/netfilter_ipv4/ipt_connmark.h linux.dev/include/linux/netfilter_ipv4/ipt_connmark.h +--- linux.old/include/linux/netfilter_ipv4/ipt_connmark.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux.dev/include/linux/netfilter_ipv4/ipt_connmark.h 2005-08-20 20:19:41.058329864 +0200 +@@ -0,0 +1,18 @@ ++#ifndef _IPT_CONNMARK_H ++#define _IPT_CONNMARK_H ++ ++/* Copyright (C) 2002,2004 MARA Systems AB <http://www.marasystems.com> ++ * by Henrik Nordstrom <hno@marasystems.com> ++ * ++ * 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 -urN linux.old/net/ipv4/netfilter/Config.in linux.dev/net/ipv4/netfilter/Config.in +--- linux.old/net/ipv4/netfilter/Config.in 2005-08-20 20:02:09.325416000 +0200 ++++ linux.dev/net/ipv4/netfilter/Config.in 2005-08-20 20:29:11.546602464 +0200 +@@ -6,7 +6,8 @@ + + tristate 'Connection tracking (required for masq/NAT)' CONFIG_IP_NF_CONNTRACK + if [ "$CONFIG_IP_NF_CONNTRACK" != "n" ]; then +- dep_tristate ' FTP protocol support' CONFIG_IP_NF_FTP $CONFIG_IP_NF_CONNTRACK ++ bool ' Connection mark tracking support' CONFIG_IP_NF_CONNTRACK_MARK ++ dep_tristate ' FTP protocol support' CONFIG_IP_NF_FTP $CONFIG_IP_NF_CONNTRACKa + dep_tristate ' Amanda protocol support' CONFIG_IP_NF_AMANDA $CONFIG_IP_NF_CONNTRACK + dep_tristate ' TFTP protocol support' CONFIG_IP_NF_TFTP $CONFIG_IP_NF_CONNTRACK + dep_tristate ' IRC protocol support' CONFIG_IP_NF_IRC $CONFIG_IP_NF_CONNTRACK +@@ -42,6 +43,9 @@ + if [ "$CONFIG_IP_NF_CONNTRACK" != "n" ]; then + dep_tristate ' Connection state match support' CONFIG_IP_NF_MATCH_STATE $CONFIG_IP_NF_CONNTRACK $CONFIG_IP_NF_IPTABLES + dep_tristate ' Connection tracking match support' CONFIG_IP_NF_MATCH_CONNTRACK $CONFIG_IP_NF_CONNTRACK $CONFIG_IP_NF_IPTABLES ++ if [ "$CONFIG_IP_NF_CONNTRACK_MARK" != "n" ]; then ++ dep_tristate ' Connection mark match support' CONFIG_IP_NF_MATCH_CONNMARK $CONFIG_IP_NF_IPTABLES ++ fi + fi + if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then + dep_tristate ' Unclean match support (EXPERIMENTAL)' CONFIG_IP_NF_MATCH_UNCLEAN $CONFIG_IP_NF_IPTABLES +@@ -125,6 +129,9 @@ + + dep_tristate ' MARK target support' CONFIG_IP_NF_TARGET_MARK $CONFIG_IP_NF_MANGLE + fi ++ if [ "$CONFIG_IP_NF_CONNTRACK_MARK" != "n" ]; then ++ dep_tristate ' CONNMARK target support' CONFIG_IP_NF_TARGET_CONNMARK $CONFIG_IP_NF_IPTABLES ++ fi + dep_tristate ' LOG target support' CONFIG_IP_NF_TARGET_LOG $CONFIG_IP_NF_IPTABLES + dep_tristate ' TTL target support' CONFIG_IP_NF_TARGET_TTL $CONFIG_IP_NF_IPTABLES + dep_tristate ' ULOG target support' CONFIG_IP_NF_TARGET_ULOG $CONFIG_IP_NF_IPTABLES +diff -urN linux.old/net/ipv4/netfilter/Makefile linux.dev/net/ipv4/netfilter/Makefile +--- linux.old/net/ipv4/netfilter/Makefile 2005-08-20 20:02:09.326416000 +0200 ++++ linux.dev/net/ipv4/netfilter/Makefile 2005-08-20 20:29:54.081136232 +0200 +@@ -93,6 +93,7 @@ + + 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_CONNTRACK) += ipt_conntrack.o + obj-$(CONFIG_IP_NF_MATCH_UNCLEAN) += ipt_unclean.o + obj-$(CONFIG_IP_NF_MATCH_TCPMSS) += ipt_tcpmss.o +@@ -110,6 +111,7 @@ + obj-$(CONFIG_IP_NF_TARGET_REDIRECT) += ipt_REDIRECT.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_CONNMARK) += ipt_CONNMARK.o + obj-$(CONFIG_IP_NF_TARGET_TTL) += ipt_TTL.o + obj-$(CONFIG_IP_NF_TARGET_ULOG) += ipt_ULOG.o + obj-$(CONFIG_IP_NF_TARGET_TCPMSS) += ipt_TCPMSS.o +diff -urN linux.old/net/ipv4/netfilter/ip_conntrack_core.c linux.dev/net/ipv4/netfilter/ip_conntrack_core.c +--- linux.old/net/ipv4/netfilter/ip_conntrack_core.c 2005-08-20 20:02:06.828795000 +0200 ++++ linux.dev/net/ipv4/netfilter/ip_conntrack_core.c 2005-08-20 20:33:23.308328864 +0200 +@@ -755,6 +755,9 @@ + __set_bit(IPS_EXPECTED_BIT, &conntrack->status); + conntrack->master = expected; + expected->sibling = conntrack; ++#ifdef 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 -urN linux.old/net/ipv4/netfilter/ip_conntrack_standalone.c linux.dev/net/ipv4/netfilter/ip_conntrack_standalone.c +--- linux.old/net/ipv4/netfilter/ip_conntrack_standalone.c 2005-08-20 20:02:06.583833000 +0200 ++++ linux.dev/net/ipv4/netfilter/ip_conntrack_standalone.c 2005-08-20 20:32:15.364657872 +0200 +@@ -107,6 +107,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 + + #if defined(CONFIG_IP_NF_MATCH_LAYER7) || defined(CONFIG_IP_NF_MATCH_LAYER7_MODULE) + if(conntrack->layer7.app_proto) +diff -urN linux.old/net/ipv4/netfilter/ipt_CONNMARK.c linux.dev/net/ipv4/netfilter/ipt_CONNMARK.c +--- linux.old/net/ipv4/netfilter/ipt_CONNMARK.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux.dev/net/ipv4/netfilter/ipt_CONNMARK.c 2005-08-20 20:21:28.666970864 +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 <http://www.marasystems.com> ++ * by Henrik Nordstrom <hno@marasystems.com> ++ * ++ * 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 <linux/module.h> ++#include <linux/skbuff.h> ++#include <linux/ip.h> ++#include <net/checksum.h> ++ ++MODULE_AUTHOR("Henrik Nordstrom <hno@marasytems.com>"); ++MODULE_DESCRIPTION("IP tables CONNMARK matching module"); ++MODULE_LICENSE("GPL"); ++ ++#include <linux/netfilter_ipv4/ip_tables.h> ++#include <linux/netfilter_ipv4/ipt_CONNMARK.h> ++#include <linux/netfilter_ipv4/ip_conntrack.h> ++ ++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) ++{ ++ 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 -urN linux.old/net/ipv4/netfilter/ipt_connmark.c linux.dev/net/ipv4/netfilter/ipt_connmark.c +--- linux.old/net/ipv4/netfilter/ipt_connmark.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux.dev/net/ipv4/netfilter/ipt_connmark.c 2005-08-20 20:21:28.666970864 +0200 +@@ -0,0 +1,83 @@ ++/* This kernel module matches connection mark values set by the ++ * CONNMARK target ++ * ++ * Copyright (C) 2002,2004 MARA Systems AB <http://www.marasystems.com> ++ * by Henrik Nordstrom <hno@marasystems.com> ++ * ++ * 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 <linux/module.h> ++#include <linux/skbuff.h> ++ ++MODULE_AUTHOR("Henrik Nordstrom <hno@marasytems.com>"); ++MODULE_DESCRIPTION("IP tables connmark match module"); ++MODULE_LICENSE("GPL"); ++ ++#include <linux/netfilter_ipv4/ip_tables.h> ++#include <linux/netfilter_ipv4/ipt_connmark.h> ++#include <linux/netfilter_ipv4/ip_conntrack.h> ++ ++static int ++match(const struct sk_buff *skb, ++ const struct net_device *in, ++ const struct net_device *out, ++ const void *matchinfo, ++ int offset, ++ const void *hdr, ++ u_int16_t datalen, ++ int *hotdrop) ++{ ++ const struct ipt_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 --git a/openwrt/target/linux/linux-2.4/patches/generic/608-netfilter_ipset.patch b/openwrt/target/linux/linux-2.4/patches/generic/608-netfilter_ipset.patch new file mode 100644 index 000000000..8cf5c694d --- /dev/null +++ b/openwrt/target/linux/linux-2.4/patches/generic/608-netfilter_ipset.patch @@ -0,0 +1,5725 @@ +diff -Naur linux-old/Documentation/Configure.help linux-new/Documentation/Configure.help +--- linux-old/Documentation/Configure.help 2005-07-23 23:34:46.000000000 +0200 ++++ linux-new/Documentation/Configure.help 2005-08-10 22:13:01.000000000 +0200 +@@ -3209,6 +3209,75 @@ + If you want to compile it as a module, say M here and read + <file:Documentation/modules.txt>. If unsure, say `N'. + ++IP set support ++CONFIG_IP_NF_SET ++ This option adds IP set support to the kernel. ++ ++ In order to define and use sets, you need userlevel utilities: an ++ iptables binary which knows about IP sets and the program ipset(8), ++ by which you can define and setup the sets themselves. ++ ++ If you want to compile it as a module, say M here and read ++ <file:Documentation/modules.txt>. If unsure, say `N'. ++ ++set match support ++CONFIG_IP_NF_MATCH_SET ++ This option adds IP set match support. ++ You need the ipset utility to create and set up the sets. ++ ++ If you want to compile it as a module, say M here and read ++ <file:Documentation/modules.txt>. If unsure, say `N'. ++ ++SET target support ++CONFIG_IP_NF_TARGET_SET ++ This option adds IP set target support. ++ You need the ipset utility to create and set up the sets. ++ ++ If you want to compile it as a module, say M here and read ++ <file:Documentation/modules.txt>. If unsure, say `N'. ++ ++ipmap set type support ++CONFIG_IP_NF_SET_IPMAP ++ This option adds the ipmap set type support. ++ ++ If you want to compile it as a module, say M here and read ++ <file:Documentation/modules.txt>. If unsure, say `N'. ++ ++macipmap set type support ++CONFIG_IP_NF_SET_MACIPMAP ++ This option adds the macipmap set type support. ++ ++ If you want to compile it as a module, say M here and read ++ <file:Documentation/modules.txt>. If unsure, say `N'. ++ ++portmap set type support ++CONFIG_IP_NF_SET_PORTMAP ++ This option adds the portmap set type support. ++ ++ If you want to compile it as a module, say M here and read ++ <file:Documentation/modules.txt>. If unsure, say `N'. ++ ++iphash set type support ++CONFIG_IP_NF_SET_IPHASH ++ This option adds the iphash set type support. ++ ++ If you want to compile it as a module, say M here and read ++ <file:Documentation/modules.txt>. If unsure, say `N'. ++ ++nethash set type support ++CONFIG_IP_NF_SET_NETHASH ++ This option adds the nethash set type support. ++ ++ If you want to compile it as a module, say M here and read ++ <file:Documentation/modules.txt>. If unsure, say `N'. ++ ++iptree set type support ++CONFIG_IP_NF_SET_IPTREE ++ This option adds the iptree set type support. ++ ++ If you want to compile it as a module, say M here and read ++ <file:Documentation/modules.txt>. If unsure, say `N'. ++ + TTL target support + CONFIG_IP_NF_TARGET_TTL + This option adds a `TTL' target, which enables the user to set +diff -Naur linux-old/include/linux/netfilter_ipv4/ip_set.h linux-new/include/linux/netfilter_ipv4/ip_set.h +--- linux-old/include/linux/netfilter_ipv4/ip_set.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-new/include/linux/netfilter_ipv4/ip_set.h 2005-08-10 22:13:00.000000000 +0200 +@@ -0,0 +1,489 @@ ++#ifndef _IP_SET_H ++#define _IP_SET_H ++ ++/* Copyright (C) 2000-2002 Joakim Axelsson <gozem@linux.nu> ++ * Patrick Schaaf <bof@bof.de> ++ * Martin Josefsson <gandalf@wlug.westbo.se> ++ * Copyright (C) 2003-2004 Jozsef Kadlecsik <kadlec@blackhole.kfki.hu> ++ * ++ * 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. ++ */ ++ ++/* ++ * A sockopt of such quality has hardly ever been seen before on the open ++ * market! This little beauty, hardly ever used: above 64, so it's ++ * traditionally used for firewalling, not touched (even once!) by the ++ * 2.0, 2.2 and 2.4 kernels! ++ * ++ * Comes with its own certificate of authenticity, valid anywhere in the ++ * Free world! ++ * ++ * Rusty, 19.4.2000 ++ */ ++#define SO_IP_SET 83 ++ ++/* ++ * Heavily modify by Joakim Axelsson 08.03.2002 ++ * - Made it more modulebased ++ * ++ * Additional heavy modifications by Jozsef Kadlecsik 22.02.2004 ++ * - bindings added ++ * - in order to "deal with" backward compatibility, renamed to ipset ++ */ ++ ++/* ++ * Used so that the kernel module and ipset-binary can match their versions ++ */ ++#define IP_SET_PROTOCOL_VERSION 2 ++ ++#define IP_SET_MAXNAMELEN 32 /* set names and set typenames */ ++ ++/* Lets work with our own typedef for representing an IP address. ++ * We hope to make the code more portable, possibly to IPv6... ++ * ++ * The representation works in HOST byte order, because most set types ++ * will perform arithmetic operations and compare operations. ++ * ++ * For now the type is an uint32_t. ++ * ++ * Make sure to ONLY use the functions when translating and parsing ++ * in order to keep the host byte order and make it more portable: ++ * parse_ip() ++ * parse_mask() ++ * parse_ipandmask() ++ * ip_tostring() ++ * (Joakim: where are they???) ++ */ ++ ++typedef uint32_t ip_set_ip_t; ++ ++/* Sets are identified by an id in kernel space. Tweak with ip_set_id_t ++ * and IP_SET_INVALID_ID if you want to increase the max number of sets. ++ */ ++typedef uint16_t ip_set_id_t; ++ ++#define IP_SET_INVALID_ID 65535 ++ ++/* How deep we follow bindings */ ++#define IP_SET_MAX_BINDINGS 6 ++ ++/* ++ * Option flags for kernel operations (ipt_set_info) ++ */ ++#define IPSET_SRC 0x01 /* Source match/add */ ++#define IPSET_DST 0x02 /* Destination match/add */ ++#define IPSET_MATCH_INV 0x04 /* Inverse matching */ ++ ++/* ++ * Set types (flavours) ++ */ ++#define IPSET_TYPE_IP 0 /* IP address type of set */ ++#define IPSET_TYPE_PORT 1 /* Port type of set */ ++ ++/* Reserved keywords */ ++#define IPSET_TOKEN_DEFAULT ":default:" ++#define IPSET_TOKEN_ALL ":all:" ++ ++/* SO_IP_SET operation constants, and their request struct types. ++ * ++ * Operation ids: ++ * 0-99: commands with version checking ++ * 100-199: add/del/test/bind/unbind ++ * 200-299: list, save, restore ++ */ ++ ++/* Single shot operations: ++ * version, create, destroy, flush, rename and swap ++ * ++ * Sets are identified by name. ++ */ ++ ++#define IP_SET_REQ_STD \ ++ unsigned op; \ ++ unsigned version; \ ++ char name[IP_SET_MAXNAMELEN] ++ ++#define IP_SET_OP_CREATE 0x00000001 /* Create a new (empty) set */ ++struct ip_set_req_create { ++ IP_SET_REQ_STD; ++ char typename[IP_SET_MAXNAMELEN]; ++}; ++ ++#define IP_SET_OP_DESTROY 0x00000002 /* Remove a (empty) set */ ++struct ip_set_req_std { ++ IP_SET_REQ_STD; ++}; ++ ++#define IP_SET_OP_FLUSH 0x00000003 /* Remove all IPs in a set */ ++/* Uses ip_set_req_std */ ++ ++#define IP_SET_OP_RENAME 0x00000004 /* Rename a set */ ++/* Uses ip_set_req_create */ ++ ++#define IP_SET_OP_SWAP 0x00000005 /* Swap two sets */ ++/* Uses ip_set_req_create */ ++ ++union ip_set_name_index { ++ char name[IP_SET_MAXNAMELEN]; ++ ip_set_id_t index; ++}; ++ ++#define IP_SET_OP_GET_BYNAME 0x00000006 /* Get set index by name */ ++struct ip_set_req_get_set { ++ unsigned op; ++ unsigned version; ++ union ip_set_name_index set; ++}; ++ ++#define IP_SET_OP_GET_BYINDEX 0x00000007 /* Get set name by index */ ++/* Uses ip_set_req_get_set */ ++ ++#define IP_SET_OP_VERSION 0x00000100 /* Ask kernel version */ ++struct ip_set_req_version { ++ unsigned op; ++ unsigned version; ++}; ++ ++/* Double shots operations: ++ * add, del, test, bind and unbind. ++ * ++ * First we query the kernel to get the index and type of the target set, ++ * then issue the command. Validity of IP is checked in kernel in order ++ * to minimalize sockopt operations. ++ */ ++ ++/* Get minimal set data for add/del/test/bind/unbind IP */ ++#define IP_SET_OP_ADT_GET 0x00000010 /* Get set and type */ ++struct ip_set_req_adt_get { ++ unsigned op; ++ unsigned version; ++ union ip_set_name_index set; ++ char typename[IP_SET_MAXNAMELEN]; ++}; ++ ++#define IP_SET_REQ_BYINDEX \ ++ unsigned op; \ ++ ip_set_id_t index; ++ ++struct ip_set_req_adt { ++ IP_SET_REQ_BYINDEX; ++}; ++ ++#define IP_SET_OP_ADD_IP 0x00000101 /* Add an IP to a set */ ++/* Uses ip_set_req_adt, with type specific addage */ ++ ++#define IP_SET_OP_DEL_IP 0x00000102 /* Remove an IP from a set */ ++/* Uses ip_set_req_adt, with type specific addage */ ++ ++#define IP_SET_OP_TEST_IP 0x00000103 /* Test an IP in a set */ ++/* Uses ip_set_req_adt, with type specific addage */ ++ ++#define IP_SET_OP_BIND_SET 0x00000104 /* Bind an IP to a set */ ++/* Uses ip_set_req_bind, with type specific addage */ ++struct ip_set_req_bind { ++ IP_SET_REQ_BYINDEX; ++ char binding[IP_SET_MAXNAMELEN]; ++}; ++ ++#define IP_SET_OP_UNBIND_SET 0x00000105 /* Unbind an IP from a set */ ++/* Uses ip_set_req_bind, with type speficic addage ++ * index = 0 means unbinding for all sets */ ++ ++#define IP_SET_OP_TEST_BIND_SET 0x00000106 /* Test binding an IP to a set */ ++/* Uses ip_set_req_bind, with type specific addage */ ++ ++/* Multiple shots operations: list, save, restore. ++ * ++ * - check kernel version and query the max number of sets ++ * - get the basic information on all sets ++ * and size required for the next step ++ * - get actual set data: header, data, bindings ++ */ ++ ++/* Get max_sets and the index of a queried set ++ */ ++#define IP_SET_OP_MAX_SETS 0x00000020 ++struct ip_set_req_max_sets { ++ unsigned op; ++ unsigned version; ++ ip_set_id_t max_sets; /* max_sets */ ++ ip_set_id_t sets; /* real number of sets */ ++ union ip_set_name_index set; /* index of set if name used */ ++}; ++ ++/* Get the id and name of the sets plus size for next step */ ++#define IP_SET_OP_LIST_SIZE 0x00000201 ++#define IP_SET_OP_SAVE_SIZE 0x00000202 ++struct ip_set_req_setnames { ++ unsigned op; ++ ip_set_id_t index; /* set to list/save */ ++ size_t size; /* size to get setdata/bindings */ ++ /* followed by sets number of struct ip_set_name_list */ ++}; ++ ++struct ip_set_name_list { ++ char name[IP_SET_MAXNAMELEN]; ++ char typename[IP_SET_MAXNAMELEN]; ++ ip_set_id_t index; ++ ip_set_id_t id; ++}; ++ ++/* The actual list operation */ ++#define IP_SET_OP_LIST 0x00000203 ++struct ip_set_req_list { ++ IP_SET_REQ_BYINDEX; ++ /* sets number of struct ip_set_list in reply */ ++}; ++ ++struct ip_set_list { ++ ip_set_id_t index; ++ ip_set_id_t binding; ++ u_int32_t ref; ++ size_t header_size; /* Set header data of header_size */ ++ size_t members_size; /* Set members data of members_size */ ++ size_t bindings_size; /* Set bindings data of bindings_size */ ++}; ++ ++struct ip_set_hash_list { ++ ip_set_ip_t ip; ++ ip_set_id_t binding; ++}; ++ ++/* The save operation */ ++#define IP_SET_OP_SAVE 0x00000204 ++/* Uses ip_set_req_list, in the reply replaced by ++ * sets number of struct ip_set_save plus a marker ++ * ip_set_save followed by ip_set_hash_save structures. ++ */ ++struct ip_set_save { ++ ip_set_id_t index; ++ ip_set_id_t binding; ++ size_t header_size; /* Set header data of header_size */ ++ size_t members_size; /* Set members data of members_size */ ++}; ++ ++/* At restoring, ip == 0 means default binding for the given set: */ ++struct ip_set_hash_save { ++ ip_set_ip_t ip; ++ ip_set_id_t id; ++ ip_set_id_t binding; ++}; ++ ++/* The restore operation */ ++#define IP_SET_OP_RESTORE 0x00000205 ++/* Uses ip_set_req_setnames followed by ip_set_restore structures ++ * plus a marker ip_set_restore, followed by ip_set_hash_save ++ * structures. ++ */ ++struct ip_set_restore { ++ char name[IP_SET_MAXNAMELEN]; ++ char typename[IP_SET_MAXNAMELEN]; ++ ip_set_id_t index; ++ size_t header_size; /* Create data of header_size */ ++ size_t members_size; /* Set members data of members_size */ ++}; ++ ++static inline int bitmap_bytes(ip_set_ip_t a, ip_set_ip_t b) ++{ ++ return 4 * ((((b - a + 8) / 8) + 3) / 4); ++} ++ ++#ifdef __KERNEL__ ++ ++#define ip_set_printk(format, args...) \ ++ do { \ ++ printk("%s: %s: ", __FILE__, __FUNCTION__); \ ++ printk(format "\n" , ## args); \ ++ } while (0) ++ ++#if defined(IP_SET_DEBUG) ++#define DP(format, args...) \ ++ do { \ ++ printk("%s: %s (DBG): ", __FILE__, __FUNCTION__);\ ++ printk(format "\n" , ## args); \ ++ } while (0) ++#define IP_SET_ASSERT(x) \ ++ do { \ ++ if (!(x)) \ ++ printk("IP_SET_ASSERT: %s:%i(%s)\n", \ ++ __FILE__, __LINE__, __FUNCTION__); \ ++ } while (0) ++#else ++#define DP(format, args...) ++#define IP_SET_ASSERT(x) ++#endif ++ ++struct ip_set; ++ ++/* ++ * The ip_set_type definition - one per set type, e.g. "ipmap". ++ * ++ * Each individual set has a pointer, set->type, going to one ++ * of these structures. Function pointers inside the structure implement ++ * the real behaviour of the sets. ++ * ++ * If not mentioned differently, the implementation behind the function ++ * pointers of a set_type, is expected to return 0 if ok, and a negative ++ * errno (e.g. -EINVAL) on error. ++ */ ++struct ip_set_type { ++ struct list_head list; /* next in list of set types */ ++ ++ /* test for IP in set (kernel: iptables -m set src|dst) ++ * return 0 if not in set, 1 if in set. ++ */ ++ int (*testip_kernel) (struct ip_set *set, ++ const struct sk_buff * skb, ++ u_int32_t flags, ++ ip_set_ip_t *ip); ++ ++ /* test for IP in set (userspace: ipset -T set IP) ++ * return 0 if not in set, 1 if in set. ++ */ ++ int (*testip) (struct ip_set *set, ++ const void *data, size_t size, ++ ip_set_ip_t *ip); ++ ++ /* ++ * Size of the data structure passed by when ++ * adding/deletin/testing an entry. ++ */ ++ size_t reqsize; ++ ++ /* Add IP into set (userspace: ipset -A set IP) ++ * Return -EEXIST if the address is already in the set, ++ * and -ERANGE if the address lies outside the set bounds. ++ * If the address was not already in the set, 0 is returned. ++ */ ++ int (*addip) (struct ip_set *set, ++ const void *data, size_t size, ++ ip_set_ip_t *ip); ++ ++ /* Add IP into set (kernel: iptables ... -j SET set src|dst) ++ * Return -EEXIST if the address is already in the set, ++ * and -ERANGE if the address lies outside the set bounds. ++ * If the address was not already in the set, 0 is returned. ++ */ ++ int (*addip_kernel) (struct ip_set *set, ++ const struct sk_buff * skb, ++ u_int32_t flags, ++ ip_set_ip_t *ip); ++ ++ /* remove IP from set (userspace: ipset -D set --entry x) ++ * Return -EEXIST if the address is NOT in the set, ++ * and -ERANGE if the address lies outside the set bounds. ++ * If the address really was in the set, 0 is returned. ++ */ ++ int (*delip) (struct ip_set *set, ++ const void *data, size_t size, ++ ip_set_ip_t *ip); ++ ++ /* remove IP from set (kernel: iptables ... -j SET --entry x) ++ * Return -EEXIST if the address is NOT in the set, ++ * and -ERANGE if the address lies outside the set bounds. ++ * If the address really was in the set, 0 is returned. ++ */ ++ int (*delip_kernel) (struct ip_set *set, ++ const struct sk_buff * skb, ++ u_int32_t flags, ++ ip_set_ip_t *ip); ++ ++ /* new set creation - allocated type specific items ++ */ ++ int (*create) (struct ip_set *set, ++ const void *data, size_t size); ++ ++ /* retry the operation after successfully tweaking the set ++ */ ++ int (*retry) (struct ip_set *set); ++ ++ /* set destruction - free type specific items ++ * There is no return value. ++ * Can be called only when child sets are destroyed. ++ */ ++ void (*destroy) (struct ip_set *set); ++ ++ /* set flushing - reset all bits in the set, or something similar. ++ * There is no return value. ++ */ ++ void (*flush) (struct ip_set *set); ++ ++ /* Listing: size needed for header ++ */ ++ size_t header_size; ++ ++ /* Listing: Get the header ++ * ++ * Fill in the information in "data". ++ * This function is always run after list_header_size() under a ++ * writelock on the set. Therefor is the length of "data" always ++ * correct. ++ */ ++ void (*list_header) (const struct ip_set *set, ++ void *data); ++ ++ /* Listing: Get the size for the set members ++ */ ++ int (*list_members_size) (const struct ip_set *set); ++ ++ /* Listing: Get the set members ++ * ++ * Fill in the information in "data". ++ * This function is always run after list_member_size() under a ++ * writelock on the set. Therefor is the length of "data" always ++ * correct. ++ */ ++ void (*list_members) (const struct ip_set *set, ++ void *data); ++ ++ char typename[IP_SET_MAXNAMELEN]; ++ char typecode; ++ int protocol_version; ++ ++ /* Set this to THIS_MODULE if you are a module, otherwise NULL */ ++ struct module *me; ++}; ++ ++extern int ip_set_register_set_type(struct ip_set_type *set_type); ++extern void ip_set_unregister_set_type(struct ip_set_type *set_type); ++ ++/* A generic ipset */ ++struct ip_set { ++ char name[IP_SET_MAXNAMELEN]; /* the name of the set */ ++ rwlock_t lock; /* lock for concurrency control */ ++ ip_set_id_t id; /* set id for swapping */ ++ ip_set_id_t binding; /* default binding for the set */ ++ atomic_t ref; /* in kernel and in hash references */ ++ struct ip_set_type *type; /* the set types */ ++ void *data; /* pooltype specific data */ ++}; ++ ++/* Structure to bind set elements to sets */ ++struct ip_set_hash { ++ struct list_head list; /* list of clashing entries in hash */ ++ ip_set_ip_t ip; /* ip from set */ ++ ip_set_id_t id; /* set id */ ++ ip_set_id_t binding; /* set we bind the element to */ ++}; ++ ++/* register and unregister set references */ ++extern ip_set_id_t ip_set_get_byname(const char name[IP_SET_MAXNAMELEN]); ++extern ip_set_id_t ip_set_get_byindex(ip_set_id_t id); ++extern void ip_set_put(ip_set_id_t id); ++ ++/* API for iptables set match, and SET target */ ++extern void ip_set_addip_kernel(ip_set_id_t id, ++ const struct sk_buff *skb, ++ const u_int32_t *flags); ++extern void ip_set_delip_kernel(ip_set_id_t id, ++ const struct sk_buff *skb, ++ const u_int32_t *flags); ++extern int ip_set_testip_kernel(ip_set_id_t id, ++ const struct sk_buff *skb, ++ const u_int32_t *flags); ++ ++#endif /* __KERNEL__ */ ++ ++#endif /*_IP_SET_H*/ +diff -Naur linux-old/include/linux/netfilter_ipv4/ip_set_iphash.h linux-new/include/linux/netfilter_ipv4/ip_set_iphash.h +--- linux-old/include/linux/netfilter_ipv4/ip_set_iphash.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-new/include/linux/netfilter_ipv4/ip_set_iphash.h 2005-08-10 22:13:00.000000000 +0200 +@@ -0,0 +1,30 @@ ++#ifndef __IP_SET_IPHASH_H ++#define __IP_SET_IPHASH_H ++ ++#include <linux/netfilter_ipv4/ip_set.h> ++ ++#define SETTYPE_NAME "iphash" ++#define MAX_RANGE 0x0000FFFF ++ ++struct ip_set_iphash { ++ ip_set_ip_t *members; /* the iphash proper */ ++ uint32_t initval; /* initval for jhash_1word */ ++ uint32_t prime; /* prime for double hashing */ ++ uint32_t hashsize; /* hash size */ ++ uint16_t probes; /* max number of probes */ ++ uint16_t resize; /* resize factor in percent */ ++ ip_set_ip_t netmask; /* netmask */ ++}; ++ ++struct ip_set_req_iphash_create { ++ uint32_t hashsize; ++ uint16_t probes; ++ uint16_t resize; ++ ip_set_ip_t netmask; ++}; ++ ++struct ip_set_req_iphash { ++ ip_set_ip_t ip; ++}; ++ ++#endif /* __IP_SET_IPHASH_H */ +diff -Naur linux-old/include/linux/netfilter_ipv4/ip_set_ipmap.h linux-new/include/linux/netfilter_ipv4/ip_set_ipmap.h +--- linux-old/include/linux/netfilter_ipv4/ip_set_ipmap.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-new/include/linux/netfilter_ipv4/ip_set_ipmap.h 2005-08-10 22:13:00.000000000 +0200 +@@ -0,0 +1,56 @@ ++#ifndef __IP_SET_IPMAP_H ++#define __IP_SET_IPMAP_H ++ ++#include <linux/netfilter_ipv4/ip_set.h> ++ ++#define SETTYPE_NAME "ipmap" ++#define MAX_RANGE 0x0000FFFF ++ ++struct ip_set_ipmap { ++ void *members; /* the ipmap proper */ ++ ip_set_ip_t first_ip; /* host byte order, included in range */ ++ ip_set_ip_t last_ip; /* host byte order, included in range */ ++ ip_set_ip_t netmask; /* subnet netmask */ ++ ip_set_ip_t sizeid; /* size of set in IPs */ ++ u_int16_t hosts; /* number of hosts in a subnet */ ++}; ++ ++struct ip_set_req_ipmap_create { ++ ip_set_ip_t from; ++ ip_set_ip_t to; ++ ip_set_ip_t netmask; ++}; ++ ++struct ip_set_req_ipmap { ++ ip_set_ip_t ip; ++}; ++ ++unsigned int ++mask_to_bits(ip_set_ip_t mask) ++{ ++ unsigned int bits = 32; ++ ip_set_ip_t maskaddr; ++ ++ if (mask == 0xFFFFFFFF) ++ return bits; ++ ++ maskaddr = 0xFFFFFFFE; ++ while (--bits >= 0 && maskaddr != mask) ++ maskaddr <<= 1; ++ ++ return bits; ++} ++ ++ip_set_ip_t ++range_to_mask(ip_set_ip_t from, ip_set_ip_t to, unsigned int *bits) ++{ ++ ip_set_ip_t mask = 0xFFFFFFFE; ++ ++ *bits = 32; ++ while (--(*bits) >= 0 && mask && (to & mask) != from) ++ mask <<= 1; ++ ++ return mask; ++} ++ ++#endif /* __IP_SET_IPMAP_H */ +diff -Naur linux-old/include/linux/netfilter_ipv4/ip_set_iptree.h linux-new/include/linux/netfilter_ipv4/ip_set_iptree.h +--- linux-old/include/linux/netfilter_ipv4/ip_set_iptree.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-new/include/linux/netfilter_ipv4/ip_set_iptree.h 2005-08-10 22:13:00.000000000 +0200 +@@ -0,0 +1,39 @@ ++#ifndef __IP_SET_IPTREE_H ++#define __IP_SET_IPTREE_H ++ ++#include <linux/netfilter_ipv4/ip_set.h> ++ ++#define SETTYPE_NAME "iptree" ++#define MAX_RANGE 0x0000FFFF ++ ++struct ip_set_iptreed { ++ unsigned long expires[255]; /* x.x.x.ADDR */ ++}; ++ ++struct ip_set_iptreec { ++ struct ip_set_iptreed *tree[255]; /* x.x.ADDR.* */ ++}; ++ ++struct ip_set_iptreeb { ++ struct ip_set_iptreec *tree[255]; /* x.ADDR.*.* */ ++}; ++ ++struct ip_set_iptree { ++ unsigned int timeout; ++ unsigned int gc_interval; ++#ifdef __KERNEL__ ++ struct timer_list gc; ++ struct ip_set_iptreeb *tree[255]; /* ADDR.*.*.* */ ++#endif ++}; ++ ++struct ip_set_req_iptree_create { ++ unsigned int timeout; ++}; ++ ++struct ip_set_req_iptree { ++ ip_set_ip_t ip; ++ unsigned int timeout; ++}; ++ ++#endif /* __IP_SET_IPTREE_H */ +diff -Naur linux-old/include/linux/netfilter_ipv4/ip_set_jhash.h linux-new/include/linux/netfilter_ipv4/ip_set_jhash.h +--- linux-old/include/linux/netfilter_ipv4/ip_set_jhash.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-new/include/linux/netfilter_ipv4/ip_set_jhash.h 2005-08-10 22:13:00.000000000 +0200 +@@ -0,0 +1,148 @@ ++#ifndef _LINUX_IPSET_JHASH_H ++#define _LINUX_IPSET_JHASH_H ++ ++/* This is a copy of linux/jhash.h but the types u32/u8 are changed ++ * to __u32/__u8 so that the header file can be included into ++ * userspace code as well. Jozsef Kadlecsik (kadlec@blackhole.kfki.hu) ++ */ ++ ++/* jhash.h: Jenkins hash support. ++ * ++ * Copyright (C) 1996 Bob Jenkins (bob_jenkins@burtleburtle.net) ++ * ++ * http://burtleburtle.net/bob/hash/ ++ * ++ * These are the credits from Bob's sources: ++ * ++ * lookup2.c, by Bob Jenkins, December 1996, Public Domain. ++ * hash(), hash2(), hash3, and mix() are externally useful functions. ++ * Routines to test the hash are included if SELF_TEST is defined. ++ * You can use this free for any purpose. It has no warranty. ++ * ++ * Copyright (C) 2003 David S. Miller (davem@redhat.com) ++ * ++ * I've modified Bob's hash to be useful in the Linux kernel, and ++ * any bugs present are surely my fault. -DaveM ++ */ ++ ++/* NOTE: Arguments are modified. */ ++#define __jhash_mix(a, b, c) \ ++{ \ ++ a -= b; a -= c; a ^= (c>>13); \ ++ b -= c; b -= a; b ^= (a<<8); \ ++ c -= a; c -= b; c ^= (b>>13); \ ++ a -= b; a -= c; a ^= (c>>12); \ ++ b -= c; b -= a; b ^= (a<<16); \ ++ c -= a; c -= b; c ^= (b>>5); \ ++ a -= b; a -= c; a ^= (c>>3); \ ++ b -= c; b -= a; b ^= (a<<10); \ ++ c -= a; c -= b; c ^= (b>>15); \ ++} ++ ++/* The golden ration: an arbitrary value */ ++#define JHASH_GOLDEN_RATIO 0x9e3779b9 ++ ++/* The most generic version, hashes an arbitrary sequence ++ * of bytes. No alignment or length assumptions are made about ++ * the input key. ++ */ ++static inline __u32 jhash(void *key, __u32 length, __u32 initval) ++{ ++ __u32 a, b, c, len; ++ __u8 *k = key; ++ ++ len = length; ++ a = b = JHASH_GOLDEN_RATIO; ++ c = initval; ++ ++ while (len >= 12) { ++ a += (k[0] +((__u32)k[1]<<8) +((__u32)k[2]<<16) +((__u32)k[3]<<24)); ++ b += (k[4] +((__u32)k[5]<<8) +((__u32)k[6]<<16) +((__u32)k[7]<<24)); ++ c += (k[8] +((__u32)k[9]<<8) +((__u32)k[10]<<16)+((__u32)k[11]<<24)); ++ ++ __jhash_mix(a,b,c); ++ ++ k += 12; ++ len -= 12; ++ } ++ ++ c += length; ++ switch (len) { ++ case 11: c += ((__u32)k[10]<<24); ++ case 10: c += ((__u32)k[9]<<16); ++ case 9 : c += ((__u32)k[8]<<8); ++ case 8 : b += ((__u32)k[7]<<24); ++ case 7 : b += ((__u32)k[6]<<16); ++ case 6 : b += ((__u32)k[5]<<8); ++ case 5 : b += k[4]; ++ case 4 : a += ((__u32)k[3]<<24); ++ case 3 : a += ((__u32)k[2]<<16); ++ case 2 : a += ((__u32)k[1]<<8); ++ case 1 : a += k[0]; ++ }; ++ ++ __jhash_mix(a,b,c); ++ ++ return c; ++} ++ ++/* A special optimized version that handles 1 or more of __u32s. ++ * The length parameter here is the number of __u32s in the key. ++ */ ++static inline __u32 jhash2(__u32 *k, __u32 length, __u32 initval) ++{ ++ __u32 a, b, c, len; ++ ++ a = b = JHASH_GOLDEN_RATIO; ++ c = initval; ++ len = length; ++ ++ while (len >= 3) { ++ a += k[0]; ++ b += k[1]; ++ c += k[2]; ++ __jhash_mix(a, b, c); ++ k += 3; len -= 3; ++ } ++ ++ c += length * 4; ++ ++ switch (len) { ++ case 2 : b += k[1]; ++ case 1 : a += k[0]; ++ }; ++ ++ __jhash_mix(a,b,c); ++ ++ return c; ++} ++ ++ ++/* A special ultra-optimized versions that knows they are hashing exactly ++ * 3, 2 or 1 word(s). ++ * ++ * NOTE: In partilar the "c += length; __jhash_mix(a,b,c);" normally ++ * done at the end is not done here. ++ */ ++static inline __u32 jhash_3words(__u32 a, __u32 b, __u32 c, __u32 initval) ++{ ++ a += JHASH_GOLDEN_RATIO; ++ b += JHASH_GOLDEN_RATIO; ++ c += initval; ++ ++ __jhash_mix(a, b, c); ++ ++ return c; ++} ++ ++static inline __u32 jhash_2words(__u32 a, __u32 b, __u32 initval) ++{ ++ return jhash_3words(a, b, 0, initval); ++} ++ ++static inline __u32 jhash_1word(__u32 a, __u32 initval) ++{ ++ return jhash_3words(a, 0, 0, initval); ++} ++ ++#endif /* _LINUX_IPSET_JHASH_H */ +diff -Naur linux-old/include/linux/netfilter_ipv4/ip_set_macipmap.h linux-new/include/linux/netfilter_ipv4/ip_set_macipmap.h +--- linux-old/include/linux/netfilter_ipv4/ip_set_macipmap.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-new/include/linux/netfilter_ipv4/ip_set_macipmap.h 2005-08-10 22:13:00.000000000 +0200 +@@ -0,0 +1,38 @@ ++#ifndef __IP_SET_MACIPMAP_H ++#define __IP_SET_MACIPMAP_H ++ ++#include <linux/netfilter_ipv4/ip_set.h> ++ ++#define SETTYPE_NAME "macipmap" ++#define MAX_RANGE 0x0000FFFF ++ ++/* general flags */ ++#define IPSET_MACIP_MATCHUNSET 1 ++ ++/* per ip flags */ ++#define IPSET_MACIP_ISSET 1 ++ ++struct ip_set_macipmap { ++ void *members; /* the macipmap proper */ ++ ip_set_ip_t first_ip; /* host byte order, included in range */ ++ ip_set_ip_t last_ip; /* host byte order, included in range */ ++ u_int32_t flags; ++}; ++ ++struct ip_set_req_macipmap_create { ++ ip_set_ip_t from; ++ ip_set_ip_t to; ++ u_int32_t flags; ++}; ++ ++struct ip_set_req_macipmap { ++ ip_set_ip_t ip; ++ unsigned char ethernet[ETH_ALEN]; ++}; ++ ++struct ip_set_macip { ++ unsigned short flags; ++ unsigned char ethernet[ETH_ALEN]; ++}; ++ ++#endif /* __IP_SET_MACIPMAP_H */ +diff -Naur linux-old/include/linux/netfilter_ipv4/ip_set_malloc.h linux-new/include/linux/netfilter_ipv4/ip_set_malloc.h +--- linux-old/include/linux/netfilter_ipv4/ip_set_malloc.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-new/include/linux/netfilter_ipv4/ip_set_malloc.h 2005-08-10 22:13:00.000000000 +0200 +@@ -0,0 +1,27 @@ ++#ifndef _IP_SET_MALLOC_H ++#define _IP_SET_MALLOC_H ++ ++#ifdef __KERNEL__ ++ ++/* Memory allocation and deallocation */ ++static size_t max_malloc_size = 131072; /* Guaranteed: slab.c */ ++ ++static inline void * ip_set_malloc(size_t bytes) ++{ ++ if (bytes > max_malloc_size) ++ return vmalloc(bytes); ++ else ++ return kmalloc(bytes, GFP_KERNEL); ++} ++ ++static inline void ip_set_free(void * data, size_t bytes) ++{ ++ if (bytes > max_malloc_size) ++ vfree(data); ++ else ++ kfree(data); ++} ++ ++#endif /* __KERNEL__ */ ++ ++#endif /*_IP_SET_MALLOC_H*/ +diff -Naur linux-old/include/linux/netfilter_ipv4/ip_set_nethash.h linux-new/include/linux/netfilter_ipv4/ip_set_nethash.h +--- linux-old/include/linux/netfilter_ipv4/ip_set_nethash.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-new/include/linux/netfilter_ipv4/ip_set_nethash.h 2005-08-10 22:13:00.000000000 +0200 +@@ -0,0 +1,55 @@ ++#ifndef __IP_SET_NETHASH_H ++#define __IP_SET_NETHASH_H ++ ++#include <linux/netfilter_ipv4/ip_set.h> ++ ++#define SETTYPE_NAME "nethash" ++#define MAX_RANGE 0x0000FFFF ++ ++struct ip_set_nethash { ++ ip_set_ip_t *members; /* the nethash proper */ ++ uint32_t initval; /* initval for jhash_1word */ ++ uint32_t prime; /* prime for double hashing */ ++ uint32_t hashsize; /* hash size */ ++ uint16_t probes; /* max number of probes */ ++ uint16_t resize; /* resize factor in percent */ ++ unsigned char cidr[30]; /* CIDR sizes */ ++}; ++ ++struct ip_set_req_nethash_create { ++ uint32_t hashsize; ++ uint16_t probes; ++ uint16_t resize; ++}; ++ ++struct ip_set_req_nethash { ++ ip_set_ip_t ip; ++ unsigned char cidr; ++}; ++ ++static unsigned char shifts[] = {255, 253, 249, 241, 225, 193, 129, 1}; ++ ++static inline ip_set_ip_t ++pack(ip_set_ip_t ip, unsigned char cidr) ++{ ++ ip_set_ip_t addr, *paddr = &addr; ++ unsigned char n, t, *a; ++ ++ addr = htonl(ip & (0xFFFFFFFF << (32 - (cidr)))); ++#ifdef __KERNEL__ ++ DP("ip:%u.%u.%u.%u/%u", NIPQUAD(addr), cidr); ++#endif ++ n = cidr / 8; ++ t = cidr % 8; ++ a = &((unsigned char *)paddr)[n]; ++ *a = *a /(1 << (8 - t)) + shifts[t]; ++#ifdef __KERNEL__ ++ DP("n: %u, t: %u, a: %u", n, t, *a); ++ DP("ip:%u.%u.%u.%u/%u, %u.%u.%u.%u", ++ HIPQUAD(ip), cidr, NIPQUAD(addr)); ++#endif ++ ++ return ntohl(addr); ++} ++ ++#endif /* __IP_SET_NETHASH_H */ +diff -Naur linux-old/include/linux/netfilter_ipv4/ip_set_portmap.h linux-new/include/linux/netfilter_ipv4/ip_set_portmap.h +--- linux-old/include/linux/netfilter_ipv4/ip_set_portmap.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-new/include/linux/netfilter_ipv4/ip_set_portmap.h 2005-08-10 22:13:00.000000000 +0200 +@@ -0,0 +1,25 @@ ++#ifndef __IP_SET_PORTMAP_H ++#define __IP_SET_PORTMAP_H ++ ++#include <linux/netfilter_ipv4/ip_set.h> ++ ++#define SETTYPE_NAME "portmap" ++#define MAX_RANGE 0x0000FFFF ++#define INVALID_PORT (MAX_RANGE + 1) ++ ++struct ip_set_portmap { ++ void *members; /* the portmap proper */ ++ ip_set_ip_t first_port; /* host byte order, included in range */ ++ ip_set_ip_t last_port; /* host byte order, included in range */ ++}; ++ ++struct ip_set_req_portmap_create { ++ ip_set_ip_t from; ++ ip_set_ip_t to; ++}; ++ ++struct ip_set_req_portmap { ++ ip_set_ip_t port; ++}; ++ ++#endif /* __IP_SET_PORTMAP_H */ +diff -Naur linux-old/include/linux/netfilter_ipv4/ip_set_prime.h linux-new/include/linux/netfilter_ipv4/ip_set_prime.h +--- linux-old/include/linux/netfilter_ipv4/ip_set_prime.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-new/include/linux/netfilter_ipv4/ip_set_prime.h 2005-08-10 22:13:00.000000000 +0200 +@@ -0,0 +1,34 @@ ++#ifndef __IP_SET_PRIME_H ++#define __IP_SET_PRIME_H ++ ++static inline unsigned make_prime_bound(unsigned nr) ++{ ++ unsigned long long nr64 = nr; ++ unsigned long long x = 1; ++ nr = 1; ++ while (x <= nr64) { x <<= 2; nr <<= 1; } ++ return nr; ++} ++ ++static inline int make_prime_check(unsigned nr) ++{ ++ unsigned x = 3; ++ unsigned b = make_prime_bound(nr); ++ while (x <= b) { ++ if (0 == (nr % x)) return 0; ++ x += 2; ++ } ++ return 1; ++} ++ ++static unsigned make_prime(unsigned nr) ++{ ++ if (0 == (nr & 1)) nr--; ++ while (nr > 1) { ++ if (make_prime_check(nr)) return nr; ++ nr -= 2; ++ } ++ return 2; ++} ++ ++#endif /* __IP_SET_PRIME_H */ +diff -Naur linux-old/include/linux/netfilter_ipv4/ipt_set.h linux-new/include/linux/netfilter_ipv4/ipt_set.h +--- linux-old/include/linux/netfilter_ipv4/ipt_set.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-new/include/linux/netfilter_ipv4/ipt_set.h 2005-08-10 22:13:00.000000000 +0200 +@@ -0,0 +1,21 @@ ++#ifndef _IPT_SET_H ++#define _IPT_SET_H ++ ++#include <linux/netfilter_ipv4/ip_set.h> ++ ++struct ipt_set_info { ++ ip_set_id_t index; ++ u_int32_t flags[IP_SET_MAX_BINDINGS + 1]; ++}; ++ ++/* match info */ ++struct ipt_set_info_match { ++ struct ipt_set_info match_set; ++}; ++ ++struct ipt_set_info_target { ++ struct ipt_set_info add_set; ++ struct ipt_set_info del_set; ++}; ++ ++#endif /*_IPT_SET_H*/ +diff -Naur linux-old/net/ipv4/netfilter/Config.in linux-new/net/ipv4/netfilter/Config.in +--- linux-old/net/ipv4/netfilter/Config.in 2005-07-23 23:34:46.000000000 +0200 ++++ linux-new/net/ipv4/netfilter/Config.in 2005-08-10 22:13:01.000000000 +0200 +@@ -21,6 +21,20 @@ + if [ "$CONFIG_IP_NF_IPTABLES" != "n" ]; then + # The simple matches. + dep_tristate ' limit match support' CONFIG_IP_NF_MATCH_LIMIT $CONFIG_IP_NF_IPTABLES ++ ++ dep_tristate ' IP set support' CONFIG_IP_NF_SET $CONFIG_IP_NF_IPTABLES ++ if [ "$CONFIG_IP_NF_SET" != "n" ]; then ++ int ' Maximum number of sets' CONFIG_IP_NF_SET_MAX 256 ++ int ' Hash size for bindings of IP sets' CONFIG_IP_NF_SET_HASHSIZE 1024 ++ dep_tristate ' set match support' CONFIG_IP_NF_MATCH_SET $CONFIG_IP_NF_SET ++ dep_tristate ' SET target support' CONFIG_IP_NF_TARGET_SET $CONFIG_IP_NF_SET ++ dep_tristate ' ipmap set type support' CONFIG_IP_NF_SET_IPMAP $CONFIG_IP_NF_SET ++ dep_tristate ' portmap set type support' CONFIG_IP_NF_SET_PORTMAP $CONFIG_IP_NF_SET ++ dep_tristate ' macipmap set type support' CONFIG_IP_NF_SET_MACIPMAP $CONFIG_IP_NF_SET ++ dep_tristate ' iphash set type support' CONFIG_IP_NF_SET_IPHASH $CONFIG_IP_NF_SET ++ dep_tristate ' nethash set type support' CONFIG_IP_NF_SET_NETHASH $CONFIG_IP_NF_SET ++ dep_tristate ' iptree set type support' CONFIG_IP_NF_SET_IPTREE $CONFIG_IP_NF_SET ++ fi + dep_tristate ' MAC address match support' CONFIG_IP_NF_MATCH_MAC $CONFIG_IP_NF_IPTABLES + dep_tristate ' Packet type match support' CONFIG_IP_NF_MATCH_PKTTYPE $CONFIG_IP_NF_IPTABLES + dep_tristate ' netfilter MARK match support' CONFIG_IP_NF_MATCH_MARK $CONFIG_IP_NF_IPTABLES +diff -Naur linux-old/net/ipv4/netfilter/ip_set.c linux-new/net/ipv4/netfilter/ip_set.c +--- linux-old/net/ipv4/netfilter/ip_set.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-new/net/ipv4/netfilter/ip_set.c 2005-08-10 22:13:00.000000000 +0200 +@@ -0,0 +1,2002 @@ ++/* Copyright (C) 2000-2002 Joakim Axelsson <gozem@linux.nu> ++ * Patrick Schaaf <bof@bof.de> ++ * Copyright (C) 2003-2004 Jozsef Kadlecsik <kadlec@blackhole.kfki.hu> ++ * ++ * 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. ++ */ ++ ++/* Kernel module for IP set management */ ++ ++#include <linux/config.h> ++#include <linux/module.h> ++#include <linux/kmod.h> ++#include <linux/ip.h> ++#include <linux/skbuff.h> ++#include <linux/random.h> ++#include <linux/jhash.h> ++#include <linux/netfilter_ipv4/ip_tables.h> ++#include <linux/errno.h> ++#include <asm/uaccess.h> ++#include <asm/bitops.h> ++#include <asm/softirq.h> ++#include <asm/semaphore.h> ++#include <linux/spinlock.h> ++#include <linux/vmalloc.h> ++ ++#define ASSERT_READ_LOCK(x) /* dont use that */ ++#define ASSERT_WRITE_LOCK(x) ++#include <linux/netfilter_ipv4/listhelp.h> ++#include <linux/netfilter_ipv4/ip_set.h> ++ ++static struct list_head set_type_list; /* all registered sets */ ++static struct ip_set **ip_set_list; /* all individual sets */ ++static DECLARE_RWLOCK(ip_set_lock); /* protects the lists and the hash */ ++static DECLARE_MUTEX(ip_set_app_mutex); /* serializes user access */ ++static ip_set_id_t ip_set_max = CONFIG_IP_NF_SET_MAX; ++static ip_set_id_t ip_set_bindings_hash_size = CONFIG_IP_NF_SET_HASHSIZE; ++static struct list_head *ip_set_hash; /* hash of bindings */ ++static unsigned int ip_set_hash_random; /* random seed */ ++ ++/* Arrgh */ ++#ifdef MODULE ++#define __MOD_INC(foo) __MOD_INC_USE_COUNT(foo) ++#define __MOD_DEC(foo) __MOD_DEC_USE_COUNT(foo) ++#define __MOD_INC_SELF MOD_INC_USE_COUNT ++#define __MOD_DEC_SELF MOD_DEC_USE_COUNT ++#else ++#define __MOD_INC(foo) ++#define __MOD_DEC(foo) ++#define __MOD_INC_SELF ++#define __MOD_DEC_SELF ++#endif ++ ++/* ++ * Sets are identified either by the index in ip_set_list or by id. ++ * The id never changes and is used to find a key in the hash. ++ * The index may change by swapping and used at all other places ++ * (set/SET netfilter modules, binding value, etc.) ++ * ++ * Userspace requests are serialized by ip_set_mutex and sets can ++ * be deleted only from userspace. Therefore ip_set_list locking ++ * must obey the following rules: ++ * ++ * - kernel requests: read and write locking mandatory ++ * - user requests: read locking optional, write locking mandatory ++ */ ++ ++static inline void ++__ip_set_get(ip_set_id_t index) ++{ ++ atomic_inc(&ip_set_list[index]->ref); ++} ++ ++static inline void ++__ip_set_put(ip_set_id_t index) ++{ ++ atomic_dec(&ip_set_list[index]->ref); ++} ++ ++/* ++ * Binding routines ++ */ ++ ++static inline int ++ip_hash_cmp(const struct ip_set_hash *set_hash, ++ ip_set_id_t id, ip_set_ip_t ip) ++{ ++ return set_hash->id == id && set_hash->ip == ip; ++} ++ ++static ip_set_id_t ++ip_set_find_in_hash(ip_set_id_t id, ip_set_ip_t ip) ++{ ++ u_int32_t key = jhash_2words(id, ip, ip_set_hash_random) ++ % ip_set_bindings_hash_size; ++ struct ip_set_hash *set_hash; ++ ++ MUST_BE_READ_LOCKED(&ip_set_lock); ++ IP_SET_ASSERT(ip_set_list[id]); ++ DP("set: %s, ip: %u.%u.%u.%u", ip_set_list[id]->name, HIPQUAD(ip)); ++ ++ set_hash = LIST_FIND(&ip_set_hash[key], ip_hash_cmp, ++ struct ip_set_hash *, id, ip); ++ ++ DP("set: %s, ip: %u.%u.%u.%u, binding: %s", ip_set_list[id]->name, ++ HIPQUAD(ip), ++ set_hash != NULL ? ip_set_list[set_hash->binding]->name : ""); ++ ++ return (set_hash != NULL ? set_hash->binding : IP_SET_INVALID_ID); ++} ++ ++static inline void ++__set_hash_del(struct ip_set_hash *set_hash) ++{ ++ MUST_BE_WRITE_LOCKED(&ip_set_lock); ++ IP_SET_ASSERT(ip_set_list[set_hash->binding]); ++ ++ __ip_set_put(set_hash->binding); ++ list_del(&set_hash->list); ++ kfree(set_hash); ++} ++ ++static int ++ip_set_hash_del(ip_set_id_t id, ip_set_ip_t ip) ++{ ++ u_int32_t key = jhash_2words(id, ip, ip_set_hash_random) ++ % ip_set_bindings_hash_size; ++ struct ip_set_hash *set_hash; ++ ++ IP_SET_ASSERT(ip_set_list[id]); ++ DP("set: %s, ip: %u.%u.%u.%u", ip_set_list[id]->name, HIPQUAD(ip)); ++ WRITE_LOCK(&ip_set_lock); ++ set_hash = LIST_FIND(&ip_set_hash[key], ip_hash_cmp, ++ struct ip_set_hash *, id, ip); ++ DP("set: %s, ip: %u.%u.%u.%u, binding: %s", ip_set_list[id]->name, ++ HIPQUAD(ip), ++ set_hash != NULL ? ip_set_list[set_hash->binding]->name : ""); ++ ++ if (set_hash != NULL) ++ __set_hash_del(set_hash); ++ WRITE_UNLOCK(&ip_set_lock); ++ return 0; ++} ++ ++static int ++ip_set_hash_add(ip_set_id_t id, ip_set_ip_t ip, ip_set_id_t binding) ++{ ++ u_int32_t key = jhash_2words(id, ip, ip_set_hash_random) ++ % ip_set_bindings_hash_size; ++ struct ip_set_hash *set_hash; ++ int ret = 0; ++ ++ IP_SET_ASSERT(ip_set_list[id]); ++ IP_SET_ASSERT(ip_set_list[binding]); ++ DP("set: %s, ip: %u.%u.%u.%u, binding: %s", ip_set_list[id]->name, ++ HIPQUAD(ip), ip_set_list[binding]->name); ++ WRITE_LOCK(&ip_set_lock); ++ set_hash = LIST_FIND(&ip_set_hash[key], ip_hash_cmp, ++ struct ip_set_hash *, id, ip); ++ if (!set_hash) { ++ set_hash = kmalloc(sizeof(struct ip_set_hash), GFP_KERNEL); ++ if (!set_hash) { ++ ret = -ENOMEM; ++ goto unlock; ++ } ++ INIT_LIST_HEAD(&set_hash->list); ++ set_hash->id = id; ++ set_hash->ip = ip; ++ list_add(&ip_set_hash[key], &set_hash->list); ++ } else { ++ IP_SET_ASSERT(ip_set_list[set_hash->binding]); ++ DP("overwrite binding: %s", ++ ip_set_list[set_hash->binding]->name); ++ __ip_set_put(set_hash->binding); ++ } ++ set_hash->binding = binding; ++ __ip_set_get(set_hash->binding); ++ unlock: ++ WRITE_UNLOCK(&ip_set_lock); ++ return ret; ++} ++ ++#define FOREACH_HASH_DO(fn, args...) \ ++({ \ ++ ip_set_id_t __key; \ ++ struct ip_set_hash *__set_hash; \ ++ \ ++ for (__key = 0; __key < ip_set_bindings_hash_size; __key++) { \ ++ list_for_each_entry(__set_hash, &ip_set_hash[__key], list) \ ++ fn(__set_hash , ## args); \ ++ } \ ++}) ++ ++#define FOREACH_HASH_RW_DO(fn, args...) \ ++({ \ ++ ip_set_id_t __key; \ ++ struct ip_set_hash *__set_hash, *__n; \ ++ \ ++ MUST_BE_WRITE_LOCKED(&ip_set_lock); \ ++ for (__key = 0; __key < ip_set_bindings_hash_size; __key++) { \ ++ list_for_each_entry_safe(__set_hash, __n, &ip_set_hash[__key], list)\ ++ fn(__set_hash , ## args); \ ++ } \ ++}) ++ ++/* Add, del and test set entries from kernel */ ++ ++#define follow_bindings(index, set, ip) \ ++((index = ip_set_find_in_hash((set)->id, ip)) != IP_SET_INVALID_ID \ ++ || (index = (set)->binding) != IP_SET_INVALID_ID) ++ ++int ++ip_set_testip_kernel(ip_set_id_t index, ++ const struct sk_buff *skb, ++ const u_int32_t *flags) ++{ ++ struct ip_set *set; ++ ip_set_ip_t ip; ++ int res, i = 0; ++ ++ IP_SET_ASSERT(flags[i]); ++ READ_LOCK(&ip_set_lock); ++ do { ++ set = ip_set_list[index]; ++ IP_SET_ASSERT(set); ++ DP("set %s, index %u", set->name, index); ++ read_lock_bh(&set->lock); ++ res = set->type->testip_kernel(set, skb, flags[i], &ip); ++ read_unlock_bh(&set->lock); ++ } while (res > 0 ++ && flags[++i] ++ && follow_bindings(index, set, ip)); ++ READ_UNLOCK(&ip_set_lock); ++ ++ return res; ++} ++ ++void ++ip_set_addip_kernel(ip_set_id_t index, ++ const struct sk_buff *skb, ++ const u_int32_t *flags) ++{ ++ struct ip_set *set; ++ ip_set_ip_t ip; ++ int res, i= 0; ++ ++ IP_SET_ASSERT(flags[i]); ++ retry: ++ READ_LOCK(&ip_set_lock); ++ do { ++ set = ip_set_list[index]; ++ IP_SET_ASSERT(set); ++ DP("set %s, index %u", set->name, index); ++ write_lock_bh(&set->lock); ++ res = set->type->addip_kernel(set, skb, flags[i], &ip); ++ write_unlock_bh(&set->lock); ++ } while ((res == 0 || res == -EEXIST) ++ && flags[++i] ++ && follow_bindings(index, set, ip)); ++ READ_UNLOCK(&ip_set_lock); ++ ++ if (res == -EAGAIN ++ && set->type->retry ++ && (res = set->type->retry(set)) == 0) ++ goto retry; ++} ++ ++void ++ip_set_delip_kernel(ip_set_id_t index, ++ const struct sk_buff *skb, ++ const u_int32_t *flags) ++{ ++ struct ip_set *set; ++ ip_set_ip_t ip; ++ int res, i = 0; ++ ++ IP_SET_ASSERT(flags[i]); ++ READ_LOCK(&ip_set_lock); ++ do { ++ set = ip_set_list[index]; ++ IP_SET_ASSERT(set); ++ DP("set %s, index %u", set->name, index); ++ write_lock_bh(&set->lock); ++ res = set->type->delip_kernel(set, skb, flags[i], &ip); ++ write_unlock_bh(&set->lock); ++ } while ((res == 0 || res == -EEXIST) ++ && flags[++i] ++ && follow_bindings(index, set, ip)); ++ READ_UNLOCK(&ip_set_lock); ++} ++ ++/* Register and deregister settype */ ++ ++static inline int ++set_type_equal(const struct ip_set_type *set_type, const char *str2) ++{ ++ return !strncmp(set_type->typename, str2, IP_SET_MAXNAMELEN - 1); ++} ++ ++static inline struct ip_set_type * ++find_set_type(const char *name) ++{ ++ return LIST_FIND(&set_type_list, ++ set_type_equal, ++ struct ip_set_type *, ++ name); ++} ++ ++int ++ip_set_register_set_type(struct ip_set_type *set_type) ++{ ++ int ret = 0; ++ ++ if (set_type->protocol_version != IP_SET_PROTOCOL_VERSION) { ++ ip_set_printk("'%s' uses wrong protocol version %u (want %u)", ++ set_type->typename, ++ set_type->protocol_version, ++ IP_SET_PROTOCOL_VERSION); ++ return -EINVAL; ++ } ++ ++ WRITE_LOCK(&ip_set_lock); ++ if (find_set_type(set_type->typename)) { ++ /* Duplicate! */ ++ ip_set_printk("'%s' already registered!", ++ set_type->typename); ++ ret = -EINVAL; ++ goto unlock; ++ } ++ __MOD_INC_SELF; ++ list_append(&set_type_list, set_type); ++ DP("'%s' registered.", set_type->typename); ++ unlock: ++ WRITE_UNLOCK(&ip_set_lock); ++ return ret; ++} ++ ++void ++ip_set_unregister_set_type(struct ip_set_type *set_type) ++{ ++ WRITE_LOCK(&ip_set_lock); ++ if (!find_set_type(set_type->typename)) { ++ ip_set_printk("'%s' not registered?", ++ set_type->typename); ++ goto unlock; ++ } ++ LIST_DELETE(&set_type_list, set_type); ++ __MOD_DEC_SELF; ++ DP("'%s' unregistered.", set_type->typename); ++ unlock: ++ WRITE_UNLOCK(&ip_set_lock); ++ ++} ++ ++/* ++ * Userspace routines ++ */ ++ ++/* ++ * Find set by name, reference it once. The reference makes sure the ++ * thing pointed to, does not go away under our feet. Drop the reference ++ * later, using ip_set_put(). ++ */ ++ip_set_id_t ++ip_set_get_byname(const char *name) ++{ ++ ip_set_id_t i, index = IP_SET_INVALID_ID; ++ ++ down(&ip_set_app_mutex); ++ for (i = 0; i < ip_set_max; i++) { ++ if (ip_set_list[i] != NULL ++ && strcmp(ip_set_list[i]->name, name) == 0) { ++ __ip_set_get(i); ++ index = i; ++ break; ++ } ++ } ++ up(&ip_set_app_mutex); ++ return index; ++} ++ ++/* ++ * Find set by index, reference it once. The reference makes sure the ++ * thing pointed to, does not go away under our feet. Drop the reference ++ * later, using ip_set_put(). ++ */ ++ip_set_id_t ++ip_set_get_byindex(ip_set_id_t index) ++{ ++ down(&ip_set_app_mutex); ++ ++ if (index >= ip_set_max) ++ return IP_SET_INVALID_ID; ++ ++ if (ip_set_list[index]) ++ __ip_set_get(index); ++ else ++ index = IP_SET_INVALID_ID; ++ ++ up(&ip_set_app_mutex); ++ return index; ++} ++ ++/* ++ * If the given set pointer points to a valid set, decrement ++ * reference count by 1. The caller shall not assume the index ++ * to be valid, after calling this function. ++ */ ++void ip_set_put(ip_set_id_t index) ++{ ++ down(&ip_set_app_mutex); ++ if (ip_set_list[index]) ++ __ip_set_put(index); ++ up(&ip_set_app_mutex); ++} ++ ++/* Find a set by name or index */ ++static ip_set_id_t ++ip_set_find_byname(const char *name) ++{ ++ ip_set_id_t i, index = IP_SET_INVALID_ID; ++ ++ for (i = 0; i < ip_set_max; i++) { ++ if (ip_set_list[i] != NULL ++ && strcmp(ip_set_list[i]->name, name) == 0) { ++ index = i; ++ break; ++ } ++ } ++ return index; ++} ++ ++static ip_set_id_t ++ip_set_find_byindex(ip_set_id_t index) ++{ ++ if (index >= ip_set_max || ip_set_list[index] == NULL) ++ index = IP_SET_INVALID_ID; ++ ++ return index; ++} ++ ++/* ++ * Add, del, test, bind and unbind ++ */ ++ ++static inline int ++__ip_set_testip(struct ip_set *set, ++ const void *data, ++ size_t size, ++ ip_set_ip_t *ip) ++{ ++ int res; ++ ++ read_lock_bh(&set->lock); ++ res = set->type->testip(set, data, size, ip); ++ read_unlock_bh(&set->lock); ++ ++ return res; ++} ++ ++static int ++__ip_set_addip(ip_set_id_t index, ++ const void *data, ++ size_t size) ++{ ++ struct ip_set *set = ip_set_list[index]; ++ ip_set_ip_t ip; ++ int res; ++ ++ IP_SET_ASSERT(set); ++ do { ++ write_lock_bh(&set->lock); ++ res = set->type->addip(set, data, size, &ip); ++ write_unlock_bh(&set->lock); ++ } while (res == -EAGAIN ++ && set->type->retry ++ && (res = set->type->retry(set)) == 0); ++ ++ return res; ++} ++ ++static int ++ip_set_addip(ip_set_id_t index, ++ const void *data, ++ size_t size) ++{ ++ ++ return __ip_set_addip(index, ++ data + sizeof(struct ip_set_req_adt), ++ size - sizeof(struct ip_set_req_adt)); ++} ++ ++static int ++ip_set_delip(ip_set_id_t index, ++ const void *data, ++ size_t size) ++{ ++ struct ip_set *set = ip_set_list[index]; ++ ip_set_ip_t ip; ++ int res; ++ ++ IP_SET_ASSERT(set); ++ write_lock_bh(&set->lock); ++ res = set->type->delip(set, ++ data + sizeof(struct ip_set_req_adt), ++ size - sizeof(struct ip_set_req_adt), ++ &ip); ++ write_unlock_bh(&set->lock); ++ ++ return res; ++} ++ ++static int ++ip_set_testip(ip_set_id_t index, ++ const void *data, ++ size_t size) ++{ ++ struct ip_set *set = ip_set_list[index]; ++ ip_set_ip_t ip; ++ int res; ++ ++ IP_SET_ASSERT(set); ++ res = __ip_set_testip(set, ++ data + sizeof(struct ip_set_req_adt), ++ size - sizeof(struct ip_set_req_adt), ++ &ip); ++ ++ return (res > 0 ? -EEXIST : res); ++} ++ ++static int ++ip_set_bindip(ip_set_id_t index, ++ const void *data, ++ size_t size) ++{ ++ struct ip_set *set = ip_set_list[index]; ++ struct ip_set_req_bind *req_bind; ++ ip_set_id_t binding; ++ ip_set_ip_t ip; ++ int res; ++ ++ IP_SET_ASSERT(set); ++ if (size < sizeof(struct ip_set_req_bind)) ++ return -EINVAL; ++ ++ req_bind = (struct ip_set_req_bind *) data; ++ req_bind->binding[IP_SET_MAXNAMELEN - 1] = '\0'; ++ ++ if (strcmp(req_bind->binding, IPSET_TOKEN_DEFAULT) == 0) { ++ /* Default binding of a set */ ++ char *binding_name; ++ ++ if (size != sizeof(struct ip_set_req_bind) + IP_SET_MAXNAMELEN) ++ return -EINVAL; ++ ++ binding_name = (char *)(data + sizeof(struct ip_set_req_bind)); ++ binding_name[IP_SET_MAXNAMELEN - 1] = '\0'; ++ ++ binding = ip_set_find_byname(binding_name); ++ if (binding == IP_SET_INVALID_ID) ++ return -ENOENT; ++ ++ WRITE_LOCK(&ip_set_lock); ++ /* Sets as binding values are referenced */ ++ if (set->binding != IP_SET_INVALID_ID) ++ __ip_set_put(set->binding); ++ set->binding = binding; ++ __ip_set_get(set->binding); ++ WRITE_UNLOCK(&ip_set_lock); ++ ++ return 0; ++ } ++ binding = ip_set_find_byname(req_bind->binding); ++ if (binding == IP_SET_INVALID_ID) ++ return -ENOENT; ++ ++ res = __ip_set_testip(set, ++ data + sizeof(struct ip_set_req_bind), ++ size - sizeof(struct ip_set_req_bind), ++ &ip); ++ DP("set %s, ip: %u.%u.%u.%u, binding %s", ++ set->name, HIPQUAD(ip), ip_set_list[binding]->name); ++ ++ if (res >= 0) ++ res = ip_set_hash_add(set->id, ip, binding); ++ ++ return res; ++} ++ ++#define FOREACH_SET_DO(fn, args...) \ ++({ \ ++ ip_set_id_t __i; \ ++ struct ip_set *__set; \ ++ \ ++ for (__i = 0; __i < ip_set_max; __i++) { \ ++ __set = ip_set_list[__i]; \ ++ if (__set != NULL) \ ++ fn(__set , ##args); \ ++ } \ ++}) ++ ++static inline void ++__set_hash_del_byid(struct ip_set_hash *set_hash, ip_set_id_t id) ++{ ++ if (set_hash->id == id) ++ __set_hash_del(set_hash); ++} ++ ++static inline void ++__unbind_default(struct ip_set *set) ++{ ++ if (set->binding != IP_SET_INVALID_ID) { ++ /* Sets as binding values are referenced */ ++ __ip_set_put(set->binding); ++ set->binding = IP_SET_INVALID_ID; ++ } ++} ++ ++static int ++ip_set_unbindip(ip_set_id_t index, ++ const void *data, ++ size_t size) ++{ ++ struct ip_set *set; ++ struct ip_set_req_bind *req_bind; ++ ip_set_ip_t ip; ++ int res; ++ ++ DP(""); ++ if (size < sizeof(struct ip_set_req_bind)) ++ return -EINVAL; ++ ++ req_bind = (struct ip_set_req_bind *) data; ++ req_bind->binding[IP_SET_MAXNAMELEN - 1] = '\0'; ++ ++ DP("%u %s", index, req_bind->binding); ++ if (index == IP_SET_INVALID_ID) { ++ /* unbind :all: */ ++ if (strcmp(req_bind->binding, IPSET_TOKEN_DEFAULT) == 0) { ++ /* Default binding of sets */ ++ WRITE_LOCK(&ip_set_lock); ++ FOREACH_SET_DO(__unbind_default); ++ WRITE_UNLOCK(&ip_set_lock); ++ return 0; ++ } else if (strcmp(req_bind->binding, IPSET_TOKEN_ALL) == 0) { ++ /* Flush all bindings of all sets*/ ++ WRITE_LOCK(&ip_set_lock); ++ FOREACH_HASH_RW_DO(__set_hash_del); ++ WRITE_UNLOCK(&ip_set_lock); ++ return 0; ++ } ++ DP("unreachable reached!"); ++ return -EINVAL; ++ } ++ ++ set = ip_set_list[index]; ++ IP_SET_ASSERT(set); ++ if (strcmp(req_bind->binding, IPSET_TOKEN_DEFAULT) == 0) { ++ /* Default binding of set */ ++ ip_set_id_t binding = ip_set_find_byindex(set->binding); ++ ++ if (binding == IP_SET_INVALID_ID) ++ return -ENOENT; ++ ++ WRITE_LOCK(&ip_set_lock); ++ /* Sets in hash values are referenced */ ++ __ip_set_put(set->binding); ++ set->binding = IP_SET_INVALID_ID; ++ WRITE_UNLOCK(&ip_set_lock); ++ ++ return 0; ++ } else if (strcmp(req_bind->binding, IPSET_TOKEN_ALL) == 0) { ++ /* Flush all bindings */ ++ ++ WRITE_LOCK(&ip_set_lock); ++ FOREACH_HASH_RW_DO(__set_hash_del_byid, set->id); ++ WRITE_UNLOCK(&ip_set_lock); ++ return 0; ++ } ++ ++ res = __ip_set_testip(set, ++ data + sizeof(struct ip_set_req_bind), ++ size - sizeof(struct ip_set_req_bind), ++ &ip); ++ ++ DP("set %s, ip: %u.%u.%u.%u", set->name, HIPQUAD(ip)); ++ if (res >= 0) ++ res = ip_set_hash_del(set->id, ip); ++ ++ return res; ++} ++ ++static int ++ip_set_testbind(ip_set_id_t index, ++ const void *data, ++ size_t size) ++{ ++ struct ip_set *set = ip_set_list[index]; ++ struct ip_set_req_bind *req_bind; ++ ip_set_id_t binding; ++ ip_set_ip_t ip; ++ int res; ++ ++ IP_SET_ASSERT(set); ++ if (size < sizeof(struct ip_set_req_bind)) ++ return -EINVAL; ++ ++ req_bind = (struct ip_set_req_bind *) data; ++ req_bind->binding[IP_SET_MAXNAMELEN - 1] = '\0'; ++ ++ if (strcmp(req_bind->binding, IPSET_TOKEN_DEFAULT) == 0) { ++ /* Default binding of set */ ++ char *binding_name; ++ ++ if (size != sizeof(struct ip_set_req_bind) + IP_SET_MAXNAMELEN) ++ return -EINVAL; ++ ++ binding_name = (char *)(data + sizeof(struct ip_set_req_bind)); ++ binding_name[IP_SET_MAXNAMELEN - 1] = '\0'; ++ ++ binding = ip_set_find_byname(binding_name); ++ if (binding == IP_SET_INVALID_ID) ++ return -ENOENT; ++ ++ res = (set->binding == binding) ? -EEXIST : 0; ++ ++ return res; ++ } ++ binding = ip_set_find_byname(req_bind->binding); ++ if (binding == IP_SET_INVALID_ID) ++ return -ENOENT; ++ ++ ++ res = __ip_set_testip(set, ++ data + sizeof(struct ip_set_req_bind), ++ size - sizeof(struct ip_set_req_bind), ++ &ip); ++ DP("set %s, ip: %u.%u.%u.%u, binding %s", ++ set->name, HIPQUAD(ip), ip_set_list[binding]->name); ++ ++ if (res >= 0) ++ res = (ip_set_find_in_hash(set->id, ip) == binding) ++ ? -EEXIST : 0; ++ ++ return res; ++} ++ ++static struct ip_set_type * ++find_set_type_rlock(const char *typename) ++{ ++ struct ip_set_type *type; ++ ++ READ_LOCK(&ip_set_lock); ++ type = find_set_type(typename); ++ if (type == NULL) ++ READ_UNLOCK(&ip_set_lock); ++ ++ return type; ++} ++ ++static int ++find_free_id(const char *name, ++ ip_set_id_t *index, ++ ip_set_id_t *id) ++{ ++ ip_set_id_t i; ++ ++ *id = IP_SET_INVALID_ID; ++ for (i = 0; i < ip_set_max; i++) { ++ if (ip_set_list[i] == NULL) { ++ if (*id == IP_SET_INVALID_ID) ++ *id = *index = i; ++ } else if (strcmp(name, ip_set_list[i]->name) == 0) ++ /* Name clash */ ++ return -EEXIST; ++ } ++ if (*id == IP_SET_INVALID_ID) ++ /* No free slot remained */ ++ return -ERANGE; ++ /* Check that index is usable as id (swapping) */ ++ check: ++ for (i = 0; i < ip_set_max; i++) { ++ if (ip_set_list[i] != NULL ++ && ip_set_list[i]->id == *id) { ++ *id = i; ++ goto check; ++ } ++ } ++ return 0; ++} ++ ++/* ++ * Create a set ++ */ ++static int ++ip_set_create(const char *name, ++ const char *typename, ++ ip_set_id_t restore, ++ const void *data, ++ size_t size) ++{ ++ struct ip_set *set; ++ ip_set_id_t index, id; ++ int res = 0; ++ ++ DP("setname: %s, typename: %s, id: %u", name, typename, restore); ++ /* ++ * First, and without any locks, allocate and initialize ++ * a normal base set structure. ++ */ ++ set = kmalloc(sizeof(struct ip_set), GFP_KERNEL); ++ if (!set) ++ return -ENOMEM; ++ set->lock = RW_LOCK_UNLOCKED; ++ strncpy(set->name, name, IP_SET_MAXNAMELEN); ++ set->binding = IP_SET_INVALID_ID; ++ atomic_set(&set->ref, 0); ++ ++ /* ++ * Next, take the &ip_set_lock, check that we know the type, ++ * and take a reference on the type, to make sure it ++ * stays available while constructing our new set. ++ * ++ * After referencing the type, we drop the &ip_set_lock, ++ * and let the new set construction run without locks. ++ */ ++ set->type = find_set_type_rlock(typename); ++ if (set->type == NULL) { ++ /* Try loading the module */ ++ char modulename[IP_SET_MAXNAMELEN + strlen("ip_set_") + 1]; ++ strcpy(modulename, "ip_set_"); ++ strcat(modulename, typename); ++ DP("try to load %s", modulename); ++ request_module(modulename); ++ set->type = find_set_type_rlock(typename); ++ } ++ if (set->type == NULL) { ++ ip_set_printk("no set type '%s', set '%s' not created", ++ typename, name); ++ kfree(set); ++ return -ENOENT; ++ } ++ __MOD_INC(set->type->me); ++ READ_UNLOCK(&ip_set_lock); ++ ++ /* ++ * Without holding any locks, create private part. ++ */ ++ res = set->type->create(set, data, size); ++ if (res != 0) { ++ __MOD_DEC(set->type->me); ++ kfree(set); ++ return res; ++ } ++ ++ /* BTW, res==0 here. */ ++ ++ /* ++ * Here, we have a valid, constructed set. &ip_set_lock again, ++ * find free id/index and check that it is not already in ++ * ip_set_list. ++ */ ++ WRITE_LOCK(&ip_set_lock); ++ if ((res = find_free_id(set->name, &index, &id)) != 0) { ++ DP("no free id!"); ++ goto cleanup; ++ } ++ ++ /* Make sure restore gets the same index */ ++ if (restore != IP_SET_INVALID_ID && index != restore) { ++ DP("Can't restore, sets are screwed up"); ++ res = -ERANGE; ++ goto cleanup; ++ } ++ ++ /* ++ * Finally! Add our shiny new set to the list, and be done. ++ */ ++ DP("create: '%s' created with index %u, id %u!", set->name, index, id); ++ set->id = id; ++ ip_set_list[index] = set; ++ WRITE_UNLOCK(&ip_set_lock); ++ return res; ++ ++ cleanup: ++ WRITE_UNLOCK(&ip_set_lock); ++ set->type->destroy(set); ++ __MOD_DEC(set->type->me); ++ kfree(set); ++ return res; ++} ++ ++/* ++ * Destroy a given existing set ++ */ ++static void ++ip_set_destroy_set(ip_set_id_t index) ++{ ++ struct ip_set *set = ip_set_list[index]; ++ ++ IP_SET_ASSERT(set); ++ DP("set: %s", set->name); ++ WRITE_LOCK(&ip_set_lock); ++ FOREACH_HASH_RW_DO(__set_hash_del_byid, set->id); ++ if (set->binding != IP_SET_INVALID_ID) ++ __ip_set_put(set->binding); ++ ip_set_list[index] = NULL; ++ WRITE_UNLOCK(&ip_set_lock); ++ ++ /* Must call it without holding any lock */ ++ set->type->destroy(set); ++ __MOD_DEC(set->type->me); ++ kfree(set); ++} ++ ++/* ++ * Destroy a set - or all sets ++ * Sets must not be referenced/used. ++ */ ++static int ++ip_set_destroy(ip_set_id_t index) ++{ ++ ip_set_id_t i; ++ ++ /* ref modification always protected by the mutex */ ++ if (index != IP_SET_INVALID_ID) { ++ if (atomic_read(&ip_set_list[index]->ref)) ++ return -EBUSY; ++ ip_set_destroy_set(index); ++ } else { ++ for (i = 0; i < ip_set_max; i++) { ++ if (ip_set_list[i] != NULL ++ && (atomic_read(&ip_set_list[i]->ref))) ++ return -EBUSY; ++ } ++ ++ for (i = 0; i < ip_set_max; i++) { ++ if (ip_set_list[i] != NULL) ++ ip_set_destroy_set(i); ++ } ++ } ++ return 0; ++} ++ ++static void ++ip_set_flush_set(struct ip_set *set) ++{ ++ DP("set: %s %u", set->name, set->id); ++ ++ write_lock_bh(&set->lock); ++ set->type->flush(set); ++ write_unlock_bh(&set->lock); ++} ++ ++/* ++ * Flush data in a set - or in all sets ++ */ ++static int ++ip_set_flush(ip_set_id_t index) ++{ ++ if (index != IP_SET_INVALID_ID) { ++ IP_SET_ASSERT(ip_set_list[index]); ++ ip_set_flush_set(ip_set_list[index]); ++ } else ++ FOREACH_SET_DO(ip_set_flush_set); ++ ++ return 0; ++} ++ ++/* Rename a set */ ++static int ++ip_set_rename(ip_set_id_t index, const char *name) ++{ ++ struct ip_set *set = ip_set_list[index]; ++ ip_set_id_t i; ++ int res = 0; ++ ++ DP("set: %s to %s", set->name, name); ++ WRITE_LOCK(&ip_set_lock); ++ for (i = 0; i < ip_set_max; i++) { ++ if (ip_set_list[i] != NULL ++ && strncmp(ip_set_list[i]->name, ++ name, ++ IP_SET_MAXNAMELEN - 1) == 0) { ++ res = -EEXIST; ++ goto unlock; ++ } ++ } ++ strncpy(set->name, name, IP_SET_MAXNAMELEN); ++ unlock: ++ WRITE_UNLOCK(&ip_set_lock); ++ return res; ++} ++ ++/* ++ * Swap two sets so that name/index points to the other. ++ * References are also swapped. ++ */ ++static int ++ip_set_swap(ip_set_id_t from_index, ip_set_id_t to_index) ++{ ++ struct ip_set *from = ip_set_list[from_index]; ++ struct ip_set *to = ip_set_list[to_index]; ++ char from_name[IP_SET_MAXNAMELEN]; ++ u_int32_t from_ref; ++ ++ DP("set: %s to %s", from->name, to->name); ++ /* Type can't be changed. Artifical restriction. */ ++ if (from->type->typecode != to->type->typecode) ++ return -ENOEXEC; ++ ++ /* No magic here: ref munging protected by the mutex */ ++ WRITE_LOCK(&ip_set_lock); ++ strncpy(from_name, from->name, IP_SET_MAXNAMELEN); ++ from_ref = atomic_read(&from->ref); ++ ++ strncpy(from->name, to->name, IP_SET_MAXNAMELEN); ++ atomic_set(&from->ref, atomic_read(&to->ref)); ++ strncpy(to->name, from_name, IP_SET_MAXNAMELEN); ++ atomic_set(&to->ref, from_ref); ++ ++ ip_set_list[from_index] = to; ++ ip_set_list[to_index] = from; ++ ++ WRITE_UNLOCK(&ip_set_lock); ++ return 0; ++} ++ ++/* ++ * List set data ++ */ ++ ++static inline void ++__set_hash_bindings_size_list(struct ip_set_hash *set_hash, ++ ip_set_id_t id, size_t *size) ++{ ++ if (set_hash->id == id) ++ *size += sizeof(struct ip_set_hash_list); ++} ++ ++static inline void ++__set_hash_bindings_size_save(struct ip_set_hash *set_hash, ++ ip_set_id_t id, size_t *size) ++{ ++ if (set_hash->id == id) ++ *size += sizeof(struct ip_set_hash_save); ++} ++ ++static inline void ++__set_hash_bindings(struct ip_set_hash *set_hash, ++ ip_set_id_t id, void *data, int *used) ++{ ++ if (set_hash->id == id) { ++ struct ip_set_hash_list *hash_list = ++ (struct ip_set_hash_list *)(data + *used); ++ ++ hash_list->ip = set_hash->ip; ++ hash_list->binding = set_hash->binding; ++ *used += sizeof(struct ip_set_hash_list); ++ } ++} ++ ++static int ip_set_list_set(ip_set_id_t index, ++ void *data, ++ int *used, ++ int len) ++{ ++ struct ip_set *set = ip_set_list[index]; ++ struct ip_set_list *set_list; ++ ++ /* Pointer to our header */ ++ set_list = (struct ip_set_list *) (data + *used); ++ ++ DP("set: %s, used: %d %p %p", set->name, *used, data, data + *used); ++ ++ /* Get and ensure header size */ ++ if (*used + sizeof(struct ip_set_list) > len) ++ goto not_enough_mem; ++ *used += sizeof(struct ip_set_list); ++ ++ read_lock_bh(&set->lock); ++ /* Get and ensure set specific header size */ ++ set_list->header_size = set->type->header_size; ++ if (*used + set_list->header_size > len) ++ goto unlock_set; ++ ++ /* Fill in the header */ ++ set_list->index = index; ++ set_list->binding = set->binding; ++ set_list->ref = atomic_read(&set->ref); ++ ++ /* Fill in set spefific header data */ ++ DP("call list_header"); ++ set->type->list_header(set, data + *used); ++ DP("call list_header, done"); ++ *used += set_list->header_size; ++ ++ /* Get and ensure set specific members size */ ++ DP("call list_members_size"); ++ set_list->members_size = set->type->list_members_size(set); ++ DP("call list_members_size, done"); ++ if (*used + set_list->members_size > len) ++ goto unlock_set; ++ ++ /* Fill in set spefific members data */ ++ DP("call list_members"); ++ set->type->list_members(set, data + *used); ++ DP("call list_members, done"); ++ *used += set_list->members_size; ++ read_unlock_bh(&set->lock); ++ ++ /* Bindings */ ++ ++ /* Get and ensure set specific bindings size */ ++ set_list->bindings_size = 0; ++ FOREACH_HASH_DO(__set_hash_bindings_size_list, ++ set->id, &set_list->bindings_size); ++ if (*used + set_list->bindings_size > len) ++ goto not_enough_mem; ++ ++ /* Fill in set spefific bindings data */ ++ FOREACH_HASH_DO(__set_hash_bindings, set->id, data, used); ++ ++ return 0; ++ ++ unlock_set: ++ read_unlock_bh(&set->lock); ++ not_enough_mem: ++ DP("not enough mem, try again"); ++ return -EAGAIN; ++} ++ ++/* ++ * Save sets ++ */ ++static int ip_set_save_set(ip_set_id_t index, ++ void *data, ++ int *used, ++ int len) ++{ ++ struct ip_set *set; ++ struct ip_set_save *set_save; ++ ++ /* Pointer to our header */ ++ set_save = (struct ip_set_save *) (data + *used); ++ ++ /* Get and ensure header size */ ++ if (*used + sizeof(struct ip_set_save) > len) ++ goto not_enough_mem; ++ *used += sizeof(struct ip_set_save); ++ ++ set = ip_set_list[index]; ++ DP("set: %s, used: %u(%u) %p %p", set->name, *used, len, ++ data, data + *used); ++ ++ read_lock_bh(&set->lock); ++ /* Get and ensure set specific header size */ ++ set_save->header_size = set->type->header_size; ++ if (*used + set_save->header_size > len) ++ goto unlock_set; ++ ++ /* Fill in the header */ ++ set_save->index = index; ++ set_save->binding = set->binding; ++ ++ /* Fill in set spefific header data */ ++ set->type->list_header(set, data + *used); ++ *used += set_save->header_size; ++ ++ DP("set header filled: %s, used: %u %p %p", set->name, *used, ++ data, data + *used); ++ /* Get and ensure set specific members size */ ++ set_save->members_size = set->type->list_members_size(set); ++ if (*used + set_save->members_size > len) ++ goto unlock_set; ++ ++ /* Fill in set spefific members data */ ++ set->type->list_members(set, data + *used); ++ *used += set_save->members_size; ++ read_unlock_bh(&set->lock); ++ DP("set members filled: %s, used: %u %p %p", set->name, *used, ++ data, data + *used); ++ return 0; ++ ++ unlock_set: ++ read_unlock_bh(&set->lock); ++ not_enough_mem: ++ DP("not enough mem, try again"); ++ return -EAGAIN; ++} ++ ++static inline void ++__set_hash_save_bindings(struct ip_set_hash *set_hash, ++ ip_set_id_t id, ++ void *data, ++ int *used, ++ int len, ++ int *res) ++{ ++ if (*res == 0 ++ && (id == IP_SET_INVALID_ID || set_hash->id == id)) { ++ struct ip_set_hash_save *hash_save = ++ (struct ip_set_hash_save *)(data + *used); ++ /* Ensure bindings size */ ++ if (*used + sizeof(struct ip_set_hash_save) > len) { ++ *res = -ENOMEM; ++ return; ++ } ++ hash_save->id = set_hash->id; ++ hash_save->ip = set_hash->ip; ++ hash_save->binding = set_hash->binding; ++ *used += sizeof(struct ip_set_hash_save); ++ } ++} ++ ++static int ip_set_save_bindings(ip_set_id_t index, ++ void *data, ++ int *used, ++ int len) ++{ ++ int res = 0; ++ struct ip_set_save *set_save; ++ ++ DP("used %u, len %u", *used, len); ++ /* Get and ensure header size */ ++ if (*used + sizeof(struct ip_set_save) > len) ++ return -ENOMEM; ++ ++ /* Marker */ ++ set_save = (struct ip_set_save *) (data + *used); ++ set_save->index = IP_SET_INVALID_ID; ++ *used += sizeof(struct ip_set_save); ++ ++ DP("marker added used %u, len %u", *used, len); ++ /* Fill in bindings data */ ++ if (index != IP_SET_INVALID_ID) ++ /* Sets are identified by id in hash */ ++ index = ip_set_list[index]->id; ++ FOREACH_HASH_DO(__set_hash_save_bindings, index, data, used, len, &res); ++ ++ return res; ++} ++ ++/* ++ * Restore sets ++ */ ++static int ip_set_restore(void *data, ++ int len) ++{ ++ int res = 0; ++ int line = 0, used = 0, members_size; ++ struct ip_set *set; ++ struct ip_set_hash_save *hash_save; ++ struct ip_set_restore *set_restore; ++ ip_set_id_t index; ++ ++ /* Loop to restore sets */ ++ while (1) { ++ line++; ++ ++ DP("%u %u %u", used, sizeof(struct ip_set_restore), len); ++ /* Get and ensure header size */ ++ if (used + sizeof(struct ip_set_restore) > len) ++ return line; ++ set_restore = (struct ip_set_restore *) (data + used); ++ used += sizeof(struct ip_set_restore); ++ ++ /* Ensure data size */ ++ if (used ++ + set_restore->header_size ++ + set_restore->members_size > len) ++ return line; ++ ++ /* Check marker */ ++ if (set_restore->index == IP_SET_INVALID_ID) { ++ line--; ++ goto bindings; ++ } ++ ++ /* Try to create the set */ ++ DP("restore %s %s", set_restore->name, set_restore->typename); ++ res = ip_set_create(set_restore->name, ++ set_restore->typename, ++ set_restore->index, ++ data + used, ++ set_restore->header_size); ++ ++ if (res != 0) ++ return line; ++ used += set_restore->header_size; ++ ++ index = ip_set_find_byindex(set_restore->index); ++ DP("index %u, restore_index %u", index, set_restore->index); ++ if (index != set_restore->index) ++ return line; ++ /* Try to restore members data */ ++ set = ip_set_list[index]; ++ members_size = 0; ++ DP("members_size %u reqsize %u", ++ set_restore->members_size, set->type->reqsize); ++ while (members_size + set->type->reqsize <= ++ set_restore->members_size) { ++ line++; ++ DP("members: %u, line %u", members_size, line); ++ res = __ip_set_addip(index, ++ data + used + members_size, ++ set->type->reqsize); ++ if (!(res == 0 || res == -EEXIST)) ++ return line; ++ members_size += set->type->reqsize; ++ } ++ ++ DP("members_size %u %u", ++ set_restore->members_size, members_size); ++ if (members_size != set_restore->members_size) ++ return line++; ++ used += set_restore->members_size; ++ } ++ ++ bindings: ++ /* Loop to restore bindings */ ++ while (used < len) { ++ line++; ++ ++ DP("restore binding, line %u", line); ++ /* Get and ensure size */ ++ if (used + sizeof(struct ip_set_hash_save) > len) ++ return line; ++ hash_save = (struct ip_set_hash_save *) (data + used); ++ used += sizeof(struct ip_set_hash_save); ++ ++ /* hash_save->id is used to store the index */ ++ index = ip_set_find_byindex(hash_save->id); ++ DP("restore binding index %u, id %u, %u -> %u", ++ index, hash_save->id, hash_save->ip, hash_save->binding); ++ if (index != hash_save->id) ++ return line; ++ ++ set = ip_set_list[hash_save->id]; ++ /* Null valued IP means default binding */ ++ if (hash_save->ip) ++ res = ip_set_hash_add(set->id, ++ hash_save->ip, ++ hash_save->binding); ++ else { ++ IP_SET_ASSERT(set->binding == IP_SET_INVALID_ID); ++ WRITE_LOCK(&ip_set_lock); ++ set->binding = hash_save->binding; ++ __ip_set_get(set->binding); ++ WRITE_UNLOCK(&ip_set_lock); ++ DP("default binding: %u", set->binding); ++ } ++ if (res != 0) ++ return line; ++ } ++ if (used != len) ++ return line; ++ ++ return 0; ++} ++ ++static int ++ip_set_sockfn_set(struct sock *sk, int optval, void *user, unsigned int len) ++{ ++ void *data; ++ int res = 0; /* Assume OK */ ++ unsigned *op; ++ struct ip_set_req_adt *req_adt; ++ ip_set_id_t index = IP_SET_INVALID_ID; ++ int (*adtfn)(ip_set_id_t index, ++ const void *data, size_t size); ++ struct fn_table { ++ int (*fn)(ip_set_id_t index, ++ const void *data, size_t size); ++ } adtfn_table[] = ++ { { ip_set_addip }, { ip_set_delip }, { ip_set_testip}, ++ { ip_set_bindip}, { ip_set_unbindip }, { ip_set_testbind }, ++ }; ++ ++ DP("optval=%d, user=%p, len=%d", optval, user, len); ++ if (!capable(CAP_NET_ADMIN)) ++ return -EPERM; ++ if (optval != SO_IP_SET) ++ return -EBADF; ++ if (len <= sizeof(unsigned)) { ++ ip_set_printk("short userdata (want >%zu, got %u)", ++ sizeof(unsigned), len); ++ return -EINVAL; ++ } ++ data = vmalloc(len); ++ if (!data) { ++ DP("out of mem for %u bytes", len); ++ return -ENOMEM; ++ } ++ if (copy_from_user(data, user, len) != 0) { ++ res = -EFAULT; ++ goto done; ++ } ++ if (down_interruptible(&ip_set_app_mutex)) { ++ res = -EINTR; ++ goto done; ++ } ++ ++ op = (unsigned *)data; ++ DP("op=%x", *op); ++ ++ if (*op < IP_SET_OP_VERSION) { ++ /* Check the version at the beginning of operations */ ++ struct ip_set_req_version *req_version = ++ (struct ip_set_req_version *) data; ++ if (req_version->version != IP_SET_PROTOCOL_VERSION) { ++ res = -EPROTO; ++ goto done; ++ } ++ } ++ ++ switch (*op) { ++ case IP_SET_OP_CREATE:{ ++ struct ip_set_req_create *req_create ++ = (struct ip_set_req_create *) data; ++ ++ if (len <= sizeof(struct ip_set_req_create)) { ++ ip_set_printk("short CREATE data (want >%zu, got %u)", ++ sizeof(struct ip_set_req_create), len); ++ res = -EINVAL; ++ goto done; ++ } ++ req_create->name[IP_SET_MAXNAMELEN - 1] = '\0'; ++ req_create->typename[IP_SET_MAXNAMELEN - 1] = '\0'; ++ res = ip_set_create(req_create->name, ++ req_create->typename, ++ IP_SET_INVALID_ID, ++ data + sizeof(struct ip_set_req_create), ++ len - sizeof(struct ip_set_req_create)); ++ goto done; ++ } ++ case IP_SET_OP_DESTROY:{ ++ struct ip_set_req_std *req_destroy ++ = (struct ip_set_req_std *) data; ++ ++ if (len != sizeof(struct ip_set_req_std)) { ++ ip_set_printk("invalid DESTROY data (want %zu, got %u)", ++ sizeof(struct ip_set_req_std), len); ++ res = -EINVAL; ++ goto done; ++ } ++ if (strcmp(req_destroy->name, IPSET_TOKEN_ALL) == 0) { ++ /* Destroy all sets */ ++ index = IP_SET_INVALID_ID; ++ } else { ++ req_destroy->name[IP_SET_MAXNAMELEN - 1] = '\0'; ++ index = ip_set_find_byname(req_destroy->name); ++ ++ if (index == IP_SET_INVALID_ID) { ++ res = -ENOENT; ++ goto done; ++ } ++ } ++ ++ res = ip_set_destroy(index); ++ goto done; ++ } ++ case IP_SET_OP_FLUSH:{ ++ struct ip_set_req_std *req_flush = ++ (struct ip_set_req_std *) data; ++ ++ if (len != sizeof(struct ip_set_req_std)) { ++ ip_set_printk("invalid FLUSH data (want %zu, got %u)", ++ sizeof(struct ip_set_req_std), len); ++ res = -EINVAL; ++ goto done; ++ } ++ if (strcmp(req_flush->name, IPSET_TOKEN_ALL) == 0) { ++ /* Flush all sets */ ++ index = IP_SET_INVALID_ID; ++ } else { ++ req_flush->name[IP_SET_MAXNAMELEN - 1] = '\0'; ++ index = ip_set_find_byname(req_flush->name); ++ ++ if (index == IP_SET_INVALID_ID) { ++ res = -ENOENT; ++ goto done; ++ } ++ } ++ res = ip_set_flush(index); ++ goto done; ++ } ++ case IP_SET_OP_RENAME:{ ++ struct ip_set_req_create *req_rename ++ = (struct ip_set_req_create *) data; ++ ++ if (len != sizeof(struct ip_set_req_create)) { ++ ip_set_printk("invalid RENAME data (want %zu, got %u)", ++ sizeof(struct ip_set_req_create), len); ++ res = -EINVAL; ++ goto done; ++ } ++ ++ req_rename->name[IP_SET_MAXNAMELEN - 1] = '\0'; ++ req_rename->typename[IP_SET_MAXNAMELEN - 1] = '\0'; ++ ++ index = ip_set_find_byname(req_rename->name); ++ if (index == IP_SET_INVALID_ID) { ++ res = -ENOENT; ++ goto done; ++ } ++ res = ip_set_rename(index, req_rename->typename); ++ goto done; ++ } ++ case IP_SET_OP_SWAP:{ ++ struct ip_set_req_create *req_swap ++ = (struct ip_set_req_create *) data; ++ ip_set_id_t to_index; ++ ++ if (len != sizeof(struct ip_set_req_create)) { ++ ip_set_printk("invalid SWAP data (want %zu, got %u)", ++ sizeof(struct ip_set_req_create), len); ++ res = -EINVAL; ++ goto done; ++ } ++ ++ req_swap->name[IP_SET_MAXNAMELEN - 1] = '\0'; ++ req_swap->typename[IP_SET_MAXNAMELEN - 1] = '\0'; ++ ++ index = ip_set_find_byname(req_swap->name); ++ if (index == IP_SET_INVALID_ID) { ++ res = -ENOENT; ++ goto done; ++ } ++ to_index = ip_set_find_byname(req_swap->typename); ++ if (to_index == IP_SET_INVALID_ID) { ++ res = -ENOENT; ++ goto done; ++ } ++ res = ip_set_swap(index, to_index); ++ goto done; ++ } ++ default: ++ break; /* Set identified by id */ ++ } ++ ++ /* There we may have add/del/test/bind/unbind/test_bind operations */ ++ if (*op < IP_SET_OP_ADD_IP || *op > IP_SET_OP_TEST_BIND_SET) { ++ res = -EBADMSG; ++ goto done; ++ } ++ adtfn = adtfn_table[*op - IP_SET_OP_ADD_IP].fn; ++ ++ if (len < sizeof(struct ip_set_req_adt)) { ++ ip_set_printk("short data in adt request (want >=%zu, got %u)", ++ sizeof(struct ip_set_req_adt), len); ++ res = -EINVAL; ++ goto done; ++ } ++ req_adt = (struct ip_set_req_adt *) data; ++ ++ /* -U :all: :all:|:default: uses IP_SET_INVALID_ID */ ++ if (!(*op == IP_SET_OP_UNBIND_SET ++ && req_adt->index == IP_SET_INVALID_ID)) { ++ index = ip_set_find_byindex(req_adt->index); ++ if (index == IP_SET_INVALID_ID) { ++ res = -ENOENT; ++ goto done; ++ } ++ } ++ res = adtfn(index, data, len); ++ ++ done: ++ up(&ip_set_app_mutex); ++ vfree(data); ++ if (res > 0) ++ res = 0; ++ DP("final result %d", res); ++ return res; ++} ++ ++static int ++ip_set_sockfn_get(struct sock *sk, int optval, void *user, int *len) ++{ ++ int res = 0; ++ unsigned *op; ++ ip_set_id_t index = IP_SET_INVALID_ID; ++ void *data; ++ int copylen = *len; ++ ++ DP("optval=%d, user=%p, len=%d", optval, user, *len); ++ if (!capable(CAP_NET_ADMIN)) ++ return -EPERM; ++ if (optval != SO_IP_SET) ++ return -EBADF; ++ if (*len < sizeof(unsigned)) { ++ ip_set_printk("short userdata (want >=%zu, got %d)", ++ sizeof(unsigned), *len); ++ return -EINVAL; ++ } ++ data = vmalloc(*len); ++ if (!data) { ++ DP("out of mem for %d bytes", *len); ++ return -ENOMEM; ++ } ++ if (copy_from_user(data, user, *len) != 0) { ++ res = -EFAULT; ++ goto done; ++ } ++ if (down_interruptible(&ip_set_app_mutex)) { ++ res = -EINTR; ++ goto done; ++ } ++ ++ op = (unsigned *) data; ++ DP("op=%x", *op); ++ ++ if (*op < IP_SET_OP_VERSION) { ++ /* Check the version at the beginning of operations */ ++ struct ip_set_req_version *req_version = ++ (struct ip_set_req_version *) data; ++ if (req_version->version != IP_SET_PROTOCOL_VERSION) { ++ res = -EPROTO; ++ goto done; ++ } ++ } ++ ++ switch (*op) { ++ case IP_SET_OP_VERSION: { ++ struct ip_set_req_version *req_version = ++ (struct ip_set_req_version *) data; ++ ++ if (*len != sizeof(struct ip_set_req_version)) { ++ ip_set_printk("invalid VERSION (want %zu, got %d)", ++ sizeof(struct ip_set_req_version), ++ *len); ++ res = -EINVAL; ++ goto done; ++ } ++ ++ req_version->version = IP_SET_PROTOCOL_VERSION; ++ res = copy_to_user(user, req_version, ++ sizeof(struct ip_set_req_version)); ++ goto done; ++ } ++ case IP_SET_OP_GET_BYNAME: { ++ struct ip_set_req_get_set *req_get ++ = (struct ip_set_req_get_set *) data; ++ ++ if (*len != sizeof(struct ip_set_req_get_set)) { ++ ip_set_printk("invalid GET_BYNAME (want %zu, got %d)", ++ sizeof(struct ip_set_req_get_set), *len); ++ res = -EINVAL; ++ goto done; ++ } ++ req_get->set.name[IP_SET_MAXNAMELEN - 1] = '\0'; ++ index = ip_set_find_byname(req_get->set.name); ++ req_get->set.index = index; ++ goto copy; ++ } ++ case IP_SET_OP_GET_BYINDEX: { ++ struct ip_set_req_get_set *req_get ++ = (struct ip_set_req_get_set *) data; ++ ++ if (*len != sizeof(struct ip_set_req_get_set)) { ++ ip_set_printk("invalid GET_BYINDEX (want %zu, got %d)", ++ sizeof(struct ip_set_req_get_set), *len); ++ res = -EINVAL; ++ goto done; ++ } ++ req_get->set.name[IP_SET_MAXNAMELEN - 1] = '\0'; ++ index = ip_set_find_byindex(req_get->set.index); ++ strncpy(req_get->set.name, ++ index == IP_SET_INVALID_ID ? "" ++ : ip_set_list[index]->name, IP_SET_MAXNAMELEN); ++ goto copy; ++ } ++ case IP_SET_OP_ADT_GET: { ++ struct ip_set_req_adt_get *req_get ++ = (struct ip_set_req_adt_get *) data; ++ ++ if (*len != sizeof(struct ip_set_req_adt_get)) { ++ ip_set_printk("invalid ADT_GET (want %zu, got %d)", ++ sizeof(struct ip_set_req_adt_get), *len); ++ res = -EINVAL; ++ goto done; ++ } ++ req_get->set.name[IP_SET_MAXNAMELEN - 1] = '\0'; ++ index = ip_set_find_byname(req_get->set.name); ++ if (index != IP_SET_INVALID_ID) { ++ req_get->set.index = index; ++ strncpy(req_get->typename, ++ ip_set_list[index]->type->typename, ++ IP_SET_MAXNAMELEN - 1); ++ } else { ++ res = -ENOENT; ++ goto done; ++ } ++ goto copy; ++ } ++ case IP_SET_OP_MAX_SETS: { ++ struct ip_set_req_max_sets *req_max_sets ++ = (struct ip_set_req_max_sets *) data; ++ ip_set_id_t i; ++ ++ if (*len != sizeof(struct ip_set_req_max_sets)) { ++ ip_set_printk("invalid MAX_SETS (want %zu, got %d)", ++ sizeof(struct ip_set_req_max_sets), *len); ++ res = -EINVAL; ++ goto done; ++ } ++ ++ if (strcmp(req_max_sets->set.name, IPSET_TOKEN_ALL) == 0) { ++ req_max_sets->set.index = IP_SET_INVALID_ID; ++ } else { ++ req_max_sets->set.name[IP_SET_MAXNAMELEN - 1] = '\0'; ++ req_max_sets->set.index = ++ ip_set_find_byname(req_max_sets->set.name); ++ if (req_max_sets->set.index == IP_SET_INVALID_ID) { ++ res = -ENOENT; ++ goto done; ++ } ++ } ++ req_max_sets->max_sets = ip_set_max; ++ req_max_sets->sets = 0; ++ for (i = 0; i < ip_set_max; i++) { ++ if (ip_set_list[i] != NULL) ++ req_max_sets->sets++; ++ } ++ goto copy; ++ } ++ case IP_SET_OP_LIST_SIZE: ++ case IP_SET_OP_SAVE_SIZE: { ++ struct ip_set_req_setnames *req_setnames ++ = (struct ip_set_req_setnames *) data; ++ struct ip_set_name_list *name_list; ++ struct ip_set *set; ++ ip_set_id_t i; ++ int used; ++ ++ if (*len < sizeof(struct ip_set_req_setnames)) { ++ ip_set_printk("short LIST_SIZE (want >=%zu, got %d)", ++ sizeof(struct ip_set_req_setnames), *len); ++ res = -EINVAL; ++ goto done; ++ } ++ ++ req_setnames->size = 0; ++ used = sizeof(struct ip_set_req_setnames); ++ for (i = 0; i < ip_set_max; i++) { ++ if (ip_set_list[i] == NULL) ++ continue; ++ name_list = (struct ip_set_name_list *) ++ (data + used); ++ used += sizeof(struct ip_set_name_list); ++ if (used > copylen) { ++ res = -EAGAIN; ++ goto done; ++ } ++ set = ip_set_list[i]; ++ /* Fill in index, name, etc. */ ++ name_list->index = i; ++ name_list->id = set->id; ++ strncpy(name_list->name, ++ set->name, ++ IP_SET_MAXNAMELEN - 1); ++ strncpy(name_list->typename, ++ set->type->typename, ++ IP_SET_MAXNAMELEN - 1); ++ DP("filled %s of type %s, index %u\n", ++ name_list->name, name_list->typename, ++ name_list->index); ++ if (!(req_setnames->index == IP_SET_INVALID_ID ++ || req_setnames->index == i)) ++ continue; ++ /* Update size */ ++ switch (*op) { ++ case IP_SET_OP_LIST_SIZE: { ++ req_setnames->size += sizeof(struct ip_set_list) ++ + set->type->header_size ++ + set->type->list_members_size(set); ++ FOREACH_HASH_DO(__set_hash_bindings_size_list, ++ i, &req_setnames->size); ++ break; ++ } ++ case IP_SET_OP_SAVE_SIZE: { ++ req_setnames->size += sizeof(struct ip_set_save) ++ + set->type->header_size ++ + set->type->list_members_size(set); ++ FOREACH_HASH_DO(__set_hash_bindings_size_save, ++ i, &req_setnames->size); ++ break; ++ } ++ default: ++ break; ++ } ++ } ++ if (copylen != used) { ++ res = -EAGAIN; ++ goto done; ++ } ++ goto copy; ++ } ++ case IP_SET_OP_LIST: { ++ struct ip_set_req_list *req_list ++ = (struct ip_set_req_list *) data; ++ ip_set_id_t i; ++ int used; ++ ++ if (*len < sizeof(struct ip_set_req_list)) { ++ ip_set_printk("short LIST (want >=%zu, got %d)", ++ sizeof(struct ip_set_req_list), *len); ++ res = -EINVAL; ++ goto done; ++ } ++ index = req_list->index; ++ if (index != IP_SET_INVALID_ID ++ && ip_set_find_byindex(index) != index) { ++ res = -ENOENT; ++ goto done; ++ } ++ used = 0; ++ if (index == IP_SET_INVALID_ID) { ++ /* List all sets */ ++ for (i = 0; i < ip_set_max && res == 0; i++) { ++ if (ip_set_list[i] != NULL) ++ res = ip_set_list_set(i, data, &used, *len); ++ } ++ } else { ++ /* List an individual set */ ++ res = ip_set_list_set(index, data, &used, *len); ++ } ++ if (res != 0) ++ goto done; ++ else if (copylen != used) { ++ res = -EAGAIN; ++ goto done; ++ } ++ goto copy; ++ } ++ case IP_SET_OP_SAVE: { ++ struct ip_set_req_list *req_save ++ = (struct ip_set_req_list *) data; ++ ip_set_id_t i; ++ int used; ++ ++ if (*len < sizeof(struct ip_set_req_list)) { ++ ip_set_printk("short SAVE (want >=%zu, got %d)", ++ sizeof(struct ip_set_req_list), *len); ++ res = -EINVAL; ++ goto done; ++ } ++ index = req_save->index; ++ if (index != IP_SET_INVALID_ID ++ && ip_set_find_byindex(index) != index) { ++ res = -ENOENT; ++ goto done; ++ } ++ used = 0; ++ if (index == IP_SET_INVALID_ID) { ++ /* Save all sets */ ++ for (i = 0; i < ip_set_max && res == 0; i++) { ++ if (ip_set_list[i] != NULL) ++ res = ip_set_save_set(i, data, &used, *len); ++ } ++ } else { ++ /* Save an individual set */ ++ res = ip_set_save_set(index, data, &used, *len); ++ } ++ if (res == 0) ++ res = ip_set_save_bindings(index, data, &used, *len); ++ ++ if (res != 0) ++ goto done; ++ else if (copylen != used) { ++ res = -EAGAIN; ++ goto done; ++ } ++ goto copy; ++ } ++ case IP_SET_OP_RESTORE: { ++ struct ip_set_req_setnames *req_restore ++ = (struct ip_set_req_setnames *) data; ++ int line; ++ ++ if (*len < sizeof(struct ip_set_req_setnames) ++ || *len != req_restore->size) { ++ ip_set_printk("invalid RESTORE (want =%zu, got %d)", ++ req_restore->size, *len); ++ res = -EINVAL; ++ goto done; ++ } ++ line = ip_set_restore(data + sizeof(struct ip_set_req_setnames), ++ req_restore->size - sizeof(struct ip_set_req_setnames)); ++ DP("ip_set_restore: %u", line); ++ if (line != 0) { ++ res = -EAGAIN; ++ req_restore->size = line; ++ copylen = sizeof(struct ip_set_req_setnames); ++ goto copy; ++ } ++ goto done; ++ } ++ default: ++ res = -EBADMSG; ++ goto done; ++ } /* end of switch(op) */ ++ ++ copy: ++ DP("set %s, copylen %u", index != IP_SET_INVALID_ID ++ && ip_set_list[index] ++ ? ip_set_list[index]->name ++ : ":all:", copylen); ++ if (res == 0) ++ res = copy_to_user(user, data, copylen); ++ else ++ copy_to_user(user, data, copylen); ++ ++ done: ++ up(&ip_set_app_mutex); ++ vfree(data); ++ if (res > 0) ++ res = 0; ++ DP("final result %d", res); ++ return res; ++} ++ ++static struct nf_sockopt_ops so_set = { ++ .pf = PF_INET, ++ .set_optmin = SO_IP_SET, ++ .set_optmax = SO_IP_SET + 1, ++ .set = &ip_set_sockfn_set, ++ .get_optmin = SO_IP_SET, ++ .get_optmax = SO_IP_SET + 1, ++ .get = &ip_set_sockfn_get, ++ .use = 0 ++}; ++ ++static int max_sets, hash_size; ++MODULE_PARM(max_sets, "i"); ++MODULE_PARM_DESC(max_sets, "maximal number of sets"); ++MODULE_PARM(hash_size, "i"); ++MODULE_PARM_DESC(hash_size, "hash size for bindings"); ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>"); ++MODULE_DESCRIPTION("module implementing core IP set support"); ++ ++static int __init init(void) ++{ ++ int res; ++ ip_set_id_t i; ++ ++ get_random_bytes(&ip_set_hash_random, 4); ++ if (max_sets) ++ ip_set_max = max_sets; ++ ip_set_list = vmalloc(sizeof(struct ip_set *) * ip_set_max); ++ if (!ip_set_list) { ++ printk(KERN_ERR "Unable to create ip_set_list\n"); ++ return -ENOMEM; ++ } ++ memset(ip_set_list, 0, sizeof(struct ip_set *) * ip_set_max); ++ if (hash_size) ++ ip_set_bindings_hash_size = hash_size; ++ ip_set_hash = vmalloc(sizeof(struct list_head) * ip_set_bindings_hash_size); ++ if (!ip_set_hash) { ++ printk(KERN_ERR "Unable to create ip_set_hash\n"); ++ vfree(ip_set_list); ++ return -ENOMEM; ++ } ++ for (i = 0; i < ip_set_bindings_hash_size; i++) ++ INIT_LIST_HEAD(&ip_set_hash[i]); ++ ++ INIT_LIST_HEAD(&set_type_list); ++ ++ res = nf_register_sockopt(&so_set); ++ if (res != 0) { ++ ip_set_printk("SO_SET registry failed: %d", res); ++ vfree(ip_set_list); ++ vfree(ip_set_hash); ++ return res; ++ } ++ return 0; ++} ++ ++static void __exit fini(void) ++{ ++ /* There can't be any existing set or binding. Racy. */ ++ nf_unregister_sockopt(&so_set); ++ vfree(ip_set_list); ++ vfree(ip_set_hash); ++ DP("these are the famous last words"); ++} ++ ++EXPORT_SYMBOL(ip_set_register_set_type); ++EXPORT_SYMBOL(ip_set_unregister_set_type); ++ ++EXPORT_SYMBOL(ip_set_get_byname); ++EXPORT_SYMBOL(ip_set_get_byindex); ++EXPORT_SYMBOL(ip_set_put); ++ ++EXPORT_SYMBOL(ip_set_addip_kernel); ++EXPORT_SYMBOL(ip_set_delip_kernel); ++EXPORT_SYMBOL(ip_set_testip_kernel); ++ ++module_init(init); ++module_exit(fini); +diff -Naur linux-old/net/ipv4/netfilter/ip_set_iphash.c linux-new/net/ipv4/netfilter/ip_set_iphash.c +--- linux-old/net/ipv4/netfilter/ip_set_iphash.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-new/net/ipv4/netfilter/ip_set_iphash.c 2005-08-10 22:13:00.000000000 +0200 +@@ -0,0 +1,379 @@ ++/* Copyright (C) 2003-2004 Jozsef Kadlecsik <kadlec@blackhole.kfki.hu> ++ * ++ * 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. ++ */ ++ ++/* Kernel module implementing an ip hash set */ ++ ++#include <linux/module.h> ++#include <linux/ip.h> ++#include <linux/skbuff.h> ++#include <linux/netfilter_ipv4/ip_tables.h> ++#include <linux/netfilter_ipv4/ip_set.h> ++#include <linux/errno.h> ++#include <asm/uaccess.h> ++#include <asm/bitops.h> ++#include <asm/softirq.h> ++#include <linux/spinlock.h> ++#include <linux/vmalloc.h> ++#include <linux/random.h> ++ ++#include <net/ip.h> ++ ++#include <linux/netfilter_ipv4/ip_set_malloc.h> ++#include <linux/netfilter_ipv4/ip_set_iphash.h> ++#include <linux/netfilter_ipv4/ip_set_jhash.h> ++#include <linux/netfilter_ipv4/ip_set_prime.h> ++ ++static inline __u32 ++jhash_ip(const struct ip_set_iphash *map, ip_set_ip_t ip) ++{ ++ return jhash_1word(ip, map->initval); ++} ++ ++static inline __u32 ++randhash_ip(const struct ip_set_iphash *map, ip_set_ip_t ip) ++{ ++ return (1 + ip % map->prime); ++} ++ ++static inline __u32 ++hash_id(struct ip_set *set, ip_set_ip_t ip, ip_set_ip_t *hash_ip) ++{ ++ struct ip_set_iphash *map = (struct ip_set_iphash *) set->data; ++ __u32 jhash, randhash, id; ++ u_int16_t i; ++ ++ *hash_ip = ip & map->netmask; ++ jhash = jhash_ip(map, *hash_ip); ++ randhash = randhash_ip(map, *hash_ip); ++ DP("set: %s, ip:%u.%u.%u.%u, %u.%u.%u.%u, %u.%u.%u.%u", ++ set->name, HIPQUAD(ip), HIPQUAD(*hash_ip), HIPQUAD(map->netmask)); ++ ++ for (i = 0; i < map->probes; i++) { ++ id = (jhash + i * randhash) % map->hashsize; ++ DP("hash key: %u", id); ++ if (map->members[id] == *hash_ip) ++ return id; ++ /* No shortcut at testing - there can be deleted ++ * entries. */ ++ } ++ return UINT_MAX; ++} ++ ++static inline int ++__testip(struct ip_set *set, ip_set_ip_t ip, ip_set_ip_t *hash_ip) ++{ ++ return (hash_id(set, ip, hash_ip) != UINT_MAX); ++} ++ ++static int ++testip(struct ip_set *set, const void *data, size_t size, ++ ip_set_ip_t *hash_ip) ++{ ++ struct ip_set_req_iphash *req = ++ (struct ip_set_req_iphash *) data; ++ ++ if (size != sizeof(struct ip_set_req_iphash)) { ++ ip_set_printk("data length wrong (want %zu, have %zu)", ++ sizeof(struct ip_set_req_iphash), ++ size); ++ return -EINVAL; ++ } ++ return __testip(set, req->ip, hash_ip); ++} ++ ++static int ++testip_kernel(struct ip_set *set, const struct sk_buff *skb, ++ u_int32_t flags, ip_set_ip_t *hash_ip) ++{ ++ return __testip(set, ++ ntohl(flags & IPSET_SRC ? skb->nh.iph->saddr ++ : skb->nh.iph->daddr), ++ hash_ip); ++} ++ ++static inline int ++__addip(struct ip_set_iphash *map, ip_set_ip_t ip, ip_set_ip_t *hash_ip) ++{ ++ __u32 jhash, randhash, probe; ++ u_int16_t i; ++ ++ *hash_ip = ip & map->netmask; ++ jhash = jhash_ip(map, *hash_ip); ++ randhash = randhash_ip(map, *hash_ip); ++ ++ for (i = 0; i < map->probes; i++) { ++ probe = (jhash + i * randhash) % map->hashsize; ++ if (map->members[probe] == *hash_ip) ++ return -EEXIST; ++ if (!map->members[probe]) { ++ map->members[probe] = *hash_ip; ++ return 0; ++ } ++ } ++ /* Trigger rehashing */ ++ return -EAGAIN; ++} ++ ++static int ++addip(struct ip_set *set, const void *data, size_t size, ++ ip_set_ip_t *hash_ip) ++{ ++ struct ip_set_req_iphash *req = ++ (struct ip_set_req_iphash *) data; ++ ++ if (size != sizeof(struct ip_set_req_iphash)) { ++ ip_set_printk("data length wrong (want %zu, have %zu)", ++ sizeof(struct ip_set_req_iphash), ++ size); ++ return -EINVAL; ++ } ++ return __addip((struct ip_set_iphash *) set->data, req->ip, hash_ip); ++} ++ ++static int ++addip_kernel(struct ip_set *set, const struct sk_buff *skb, ++ u_int32_t flags, ip_set_ip_t *hash_ip) ++{ ++ return __addip((struct ip_set_iphash *) set->data, ++ ntohl(flags & IPSET_SRC ? skb->nh.iph->saddr ++ : skb->nh.iph->daddr), ++ hash_ip); ++} ++ ++static int retry(struct ip_set *set) ++{ ++ struct ip_set_iphash *map = (struct ip_set_iphash *) set->data; ++ ip_set_ip_t hash_ip, *members; ++ u_int32_t i, hashsize; ++ unsigned newbytes; ++ int res; ++ struct ip_set_iphash tmp = { ++ .hashsize = map->hashsize, ++ .probes = map->probes, ++ .resize = map->resize, ++ .netmask = map->netmask, ++ }; ++ ++ if (map->resize == 0) ++ return -ERANGE; ++ ++ again: ++ res = 0; ++ ++ /* Calculate new parameters */ ++ get_random_bytes(&tmp.initval, 4); ++ hashsize = tmp.hashsize + (tmp.hashsize * map->resize)/100; ++ if (hashsize == tmp.hashsize) ++ hashsize++; ++ tmp.prime = make_prime(hashsize); ++ ++ ip_set_printk("rehashing of set %s triggered: " ++ "hashsize grows from %u to %u", ++ set->name, tmp.hashsize, hashsize); ++ tmp.hashsize = hashsize; ++ ++ newbytes = hashsize * sizeof(ip_set_ip_t); ++ tmp.members = ip_set_malloc(newbytes); ++ if (!tmp.members) { ++ DP("out of memory for %d bytes", newbytes); ++ return -ENOMEM; ++ } ++ memset(tmp.members, 0, newbytes); ++ ++ write_lock_bh(&set->lock); ++ map = (struct ip_set_iphash *) set->data; /* Play safe */ ++ for (i = 0; i < map->hashsize && res == 0; i++) { ++ if (map->members[i]) ++ res = __addip(&tmp, map->members[i], &hash_ip); ++ } ++ if (res) { ++ /* Failure, try again */ ++ write_unlock_bh(&set->lock); ++ ip_set_free(tmp.members, newbytes); ++ goto again; ++ } ++ ++ /* Success at resizing! */ ++ members = map->members; ++ hashsize = map->hashsize; ++ ++ map->initval = tmp.initval; ++ map->prime = tmp.prime; ++ map->hashsize = tmp.hashsize; ++ map->members = tmp.members; ++ write_unlock_bh(&set->lock); ++ ++ ip_set_free(members, hashsize * sizeof(ip_set_ip_t)); ++ ++ return 0; ++} ++ ++static inline int ++__delip(struct ip_set *set, ip_set_ip_t ip, ip_set_ip_t *hash_ip) ++{ ++ struct ip_set_iphash *map = (struct ip_set_iphash *) set->data; ++ ip_set_ip_t id = hash_id(set, ip, hash_ip); ++ ++ if (id == UINT_MAX) ++ return -EEXIST; ++ ++ map->members[id] = 0; ++ return 0; ++} ++ ++static int ++delip(struct ip_set *set, const void *data, size_t size, ++ ip_set_ip_t *hash_ip) ++{ ++ struct ip_set_req_iphash *req = ++ (struct ip_set_req_iphash *) data; ++ ++ if (size != sizeof(struct ip_set_req_iphash)) { ++ ip_set_printk("data length wrong (want %zu, have %zu)", ++ sizeof(struct ip_set_req_iphash), ++ size); ++ return -EINVAL; ++ } ++ return __delip(set, req->ip, hash_ip); ++} ++ ++static int ++delip_kernel(struct ip_set *set, const struct sk_buff *skb, ++ u_int32_t flags, ip_set_ip_t *hash_ip) ++{ ++ return __delip(set, ++ ntohl(flags & IPSET_SRC ? skb->nh.iph->saddr ++ : skb->nh.iph->daddr), ++ hash_ip); ++} ++ ++static int create(struct ip_set *set, const void *data, size_t size) ++{ ++ unsigned newbytes; ++ struct ip_set_req_iphash_create *req = ++ (struct ip_set_req_iphash_create *) data; ++ struct ip_set_iphash *map; ++ ++ if (size != sizeof(struct ip_set_req_iphash_create)) { ++ ip_set_printk("data length wrong (want %zu, have %zu)", ++ sizeof(struct ip_set_req_iphash_create), ++ size); ++ return -EINVAL; ++ } ++ ++ if (req->hashsize < 1) { ++ ip_set_printk("hashsize too small"); ++ return -ENOEXEC; ++ } ++ ++ map = kmalloc(sizeof(struct ip_set_iphash), GFP_KERNEL); ++ if (!map) { ++ DP("out of memory for %d bytes", ++ sizeof(struct ip_set_iphash)); ++ return -ENOMEM; ++ } ++ get_random_bytes(&map->initval, 4); ++ map->prime = make_prime(req->hashsize); ++ map->hashsize = req->hashsize; ++ map->probes = req->probes; ++ map->resize = req->resize; ++ map->netmask = req->netmask; ++ newbytes = map->hashsize * sizeof(ip_set_ip_t); ++ map->members = ip_set_malloc(newbytes); ++ if (!map->members) { ++ DP("out of memory for %d bytes", newbytes); ++ kfree(map); ++ return -ENOMEM; ++ } ++ memset(map->members, 0, newbytes); ++ ++ set->data = map; ++ return 0; ++} ++ ++static void destroy(struct ip_set *set) ++{ ++ struct ip_set_iphash *map = (struct ip_set_iphash *) set->data; ++ ++ ip_set_free(map->members, map->hashsize * sizeof(ip_set_ip_t)); ++ kfree(map); ++ ++ set->data = NULL; ++} ++ ++static void flush(struct ip_set *set) ++{ ++ struct ip_set_iphash *map = (struct ip_set_iphash *) set->data; ++ memset(map->members, 0, map->hashsize * sizeof(ip_set_ip_t)); ++} ++ ++static void list_header(const struct ip_set *set, void *data) ++{ ++ struct ip_set_iphash *map = (struct ip_set_iphash *) set->data; ++ struct ip_set_req_iphash_create *header = ++ (struct ip_set_req_iphash_create *) data; ++ ++ header->hashsize = map->hashsize; ++ header->probes = map->probes; ++ header->resize = map->resize; ++ header->netmask = map->netmask; ++} ++ ++static int list_members_size(const struct ip_set *set) ++{ ++ struct ip_set_iphash *map = (struct ip_set_iphash *) set->data; ++ ++ return (map->hashsize * sizeof(ip_set_ip_t)); ++} ++ ++static void list_members(const struct ip_set *set, void *data) ++{ ++ struct ip_set_iphash *map = (struct ip_set_iphash *) set->data; ++ int bytes = map->hashsize * sizeof(ip_set_ip_t); ++ ++ memcpy(data, map->members, bytes); ++} ++ ++static struct ip_set_type ip_set_iphash = { ++ .typename = SETTYPE_NAME, ++ .typecode = IPSET_TYPE_IP, ++ .protocol_version = IP_SET_PROTOCOL_VERSION, ++ .create = &create, ++ .destroy = &destroy, ++ .flush = &flush, ++ .reqsize = sizeof(struct ip_set_req_iphash), ++ .addip = &addip, ++ .addip_kernel = &addip_kernel, ++ .retry = &retry, ++ .delip = &delip, ++ .delip_kernel = &delip_kernel, ++ .testip = &testip, ++ .testip_kernel = &testip_kernel, ++ .header_size = sizeof(struct ip_set_req_iphash_create), ++ .list_header = &list_header, ++ .list_members_size = &list_members_size, ++ .list_members = &list_members, ++ .me = THIS_MODULE, ++}; ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>"); ++MODULE_DESCRIPTION("iphash type of IP sets"); ++ ++static int __init init(void) ++{ ++ return ip_set_register_set_type(&ip_set_iphash); ++} ++ ++static void __exit fini(void) ++{ ++ /* FIXME: possible race with ip_set_create() */ ++ ip_set_unregister_set_type(&ip_set_iphash); ++} ++ ++module_init(init); ++module_exit(fini); +diff -Naur linux-old/net/ipv4/netfilter/ip_set_ipmap.c linux-new/net/ipv4/netfilter/ip_set_ipmap.c +--- linux-old/net/ipv4/netfilter/ip_set_ipmap.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-new/net/ipv4/netfilter/ip_set_ipmap.c 2005-08-10 22:13:00.000000000 +0200 +@@ -0,0 +1,314 @@ ++/* Copyright (C) 2000-2002 Joakim Axelsson <gozem@linux.nu> ++ * Patrick Schaaf <bof@bof.de> ++ * Copyright (C) 2003-2004 Jozsef Kadlecsik <kadlec@blackhole.kfki.hu> ++ * ++ * 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. ++ */ ++ ++/* Kernel module implementing an IP set type: the single bitmap type */ ++ ++#include <linux/module.h> ++#include <linux/ip.h> ++#include <linux/skbuff.h> ++#include <linux/netfilter_ipv4/ip_tables.h> ++#include <linux/netfilter_ipv4/ip_set.h> ++#include <linux/errno.h> ++#include <asm/uaccess.h> ++#include <asm/bitops.h> ++#include <asm/softirq.h> ++#include <linux/spinlock.h> ++ ++#include <linux/netfilter_ipv4/ip_set_ipmap.h> ++ ++static inline ip_set_ip_t ++ip_to_id(const struct ip_set_ipmap *map, ip_set_ip_t ip) ++{ ++ return (ip - map->first_ip)/map->hosts; ++} ++ ++static inline int ++__testip(struct ip_set *set, ip_set_ip_t ip, ip_set_ip_t *hash_ip) ++{ ++ struct ip_set_ipmap *map = (struct ip_set_ipmap *) set->data; ++ ++ if (ip < map->first_ip || ip > map->last_ip) ++ return -ERANGE; ++ ++ *hash_ip = ip & map->netmask; ++ DP("set: %s, ip:%u.%u.%u.%u, %u.%u.%u.%u", ++ set->name, HIPQUAD(ip), HIPQUAD(*hash_ip)); ++ return !!test_bit(ip_to_id(map, *hash_ip), map->members); ++} ++ ++static int ++testip(struct ip_set *set, const void *data, size_t size, ++ ip_set_ip_t *hash_ip) ++{ ++ struct ip_set_req_ipmap *req = ++ (struct ip_set_req_ipmap *) data; ++ ++ if (size != sizeof(struct ip_set_req_ipmap)) { ++ ip_set_printk("data length wrong (want %zu, have %zu)", ++ sizeof(struct ip_set_req_ipmap), ++ size); ++ return -EINVAL; ++ } ++ return __testip(set, req->ip, hash_ip); ++} ++ ++static int ++testip_kernel(struct ip_set *set, ++ const struct sk_buff *skb, ++ u_int32_t flags, ++ ip_set_ip_t *hash_ip) ++{ ++ int res; ++ ++ DP("flag: %s src: %u.%u.%u.%u dst: %u.%u.%u.%u", ++ flags & IPSET_SRC ? "SRC" : "DST", ++ NIPQUAD(skb->nh.iph->saddr), ++ NIPQUAD(skb->nh.iph->daddr)); ++ ++ res = __testip(set, ++ ntohl(flags & IPSET_SRC ? skb->nh.iph->saddr ++ : skb->nh.iph->daddr), ++ hash_ip); ++ return (res < 0 ? 0 : res); ++} ++ ++static inline int ++__addip(struct ip_set *set, ip_set_ip_t ip, ip_set_ip_t *hash_ip) ++{ ++ struct ip_set_ipmap *map = (struct ip_set_ipmap *) set->data; ++ ++ if (ip < map->first_ip || ip > map->last_ip) ++ return -ERANGE; ++ ++ *hash_ip = ip & map->netmask; ++ DP("%u.%u.%u.%u, %u.%u.%u.%u", HIPQUAD(ip), HIPQUAD(*hash_ip)); ++ if (test_and_set_bit(ip_to_id(map, *hash_ip), map->members)) ++ return -EEXIST; ++ ++ return 0; ++} ++ ++static int ++addip(struct ip_set *set, const void *data, size_t size, ++ ip_set_ip_t *hash_ip) ++{ ++ struct ip_set_req_ipmap *req = ++ (struct ip_set_req_ipmap *) data; ++ ++ if (size != sizeof(struct ip_set_req_ipmap)) { ++ ip_set_printk("data length wrong (want %zu, have %zu)", ++ sizeof(struct ip_set_req_ipmap), ++ size); ++ return -EINVAL; ++ } ++ DP("%u.%u.%u.%u", HIPQUAD(req->ip)); ++ return __addip(set, req->ip, hash_ip); ++} ++ ++static int ++addip_kernel(struct ip_set *set, const struct sk_buff *skb, ++ u_int32_t flags, ip_set_ip_t *hash_ip) ++{ ++ return __addip(set, ++ ntohl(flags & IPSET_SRC ? skb->nh.iph->saddr ++ : skb->nh.iph->daddr), ++ hash_ip); ++} ++ ++static inline int ++__delip(struct ip_set *set, ip_set_ip_t ip, ip_set_ip_t *hash_ip) ++{ ++ struct ip_set_ipmap *map = (struct ip_set_ipmap *) set->data; ++ ++ if (ip < map->first_ip || ip > map->last_ip) ++ return -ERANGE; ++ ++ *hash_ip = ip & map->netmask; ++ DP("%u.%u.%u.%u, %u.%u.%u.%u", HIPQUAD(ip), HIPQUAD(*hash_ip)); ++ if (!test_and_clear_bit(ip_to_id(map, *hash_ip), map->members)) ++ return -EEXIST; ++ ++ return 0; ++} ++ ++static int ++delip(struct ip_set *set, const void *data, size_t size, ++ ip_set_ip_t *hash_ip) ++{ ++ struct ip_set_req_ipmap *req = ++ (struct ip_set_req_ipmap *) data; ++ ++ if (size != sizeof(struct ip_set_req_ipmap)) { ++ ip_set_printk("data length wrong (want %zu, have %zu)", ++ sizeof(struct ip_set_req_ipmap), ++ size); ++ return -EINVAL; ++ } ++ return __delip(set, req->ip, hash_ip); ++} ++ ++static int ++delip_kernel(struct ip_set *set, const struct sk_buff *skb, ++ u_int32_t flags, ip_set_ip_t *hash_ip) ++{ ++ return __delip(set, ++ ntohl(flags & IPSET_SRC ? skb->nh.iph->saddr ++ : skb->nh.iph->daddr), ++ hash_ip); ++} ++ ++static int create(struct ip_set *set, const void *data, size_t size) ++{ ++ int newbytes; ++ struct ip_set_req_ipmap_create *req = ++ (struct ip_set_req_ipmap_create *) data; ++ struct ip_set_ipmap *map; ++ ++ if (size != sizeof(struct ip_set_req_ipmap_create)) { ++ ip_set_printk("data length wrong (want %zu, have %zu)", ++ sizeof(struct ip_set_req_ipmap_create), ++ size); ++ return -EINVAL; ++ } ++ ++ DP("from %u.%u.%u.%u to %u.%u.%u.%u", ++ HIPQUAD(req->from), HIPQUAD(req->to)); ++ ++ if (req->from > req->to) { ++ DP("bad ip range"); ++ return -ENOEXEC; ++ } ++ ++ if (req->to - req->from > MAX_RANGE) { ++ ip_set_printk("range too big (max %d addresses)", ++ MAX_RANGE); ++ return -ENOEXEC; ++ } ++ ++ map = kmalloc(sizeof(struct ip_set_ipmap), GFP_KERNEL); ++ if (!map) { ++ DP("out of memory for %d bytes", ++ sizeof(struct ip_set_ipmap)); ++ return -ENOMEM; ++ } ++ map->first_ip = req->from; ++ map->last_ip = req->to; ++ map->netmask = req->netmask; ++ ++ if (req->netmask == 0xFFFFFFFF) { ++ map->hosts = 1; ++ map->sizeid = map->last_ip - map->first_ip + 1; ++ } else { ++ unsigned int mask_bits, netmask_bits; ++ ip_set_ip_t mask; ++ ++ map->first_ip &= map->netmask; /* Should we better bark? */ ++ ++ mask = range_to_mask(map->first_ip, map->last_ip, &mask_bits); ++ netmask_bits = mask_to_bits(map->netmask); ++ ++ if (!mask || netmask_bits <= mask_bits) ++ return -ENOEXEC; ++ ++ map->hosts = 2 << (32 - netmask_bits - 1); ++ map->sizeid = 2 << (netmask_bits - mask_bits - 1); ++ } ++ newbytes = bitmap_bytes(0, map->sizeid - 1); ++ map->members = kmalloc(newbytes, GFP_KERNEL); ++ if (!map->members) { ++ DP("out of memory for %d bytes", newbytes); ++ kfree(map); ++ return -ENOMEM; ++ } ++ memset(map->members, 0, newbytes); ++ ++ set->data = map; ++ return 0; ++} ++ ++static void destroy(struct ip_set *set) ++{ ++ struct ip_set_ipmap *map = (struct ip_set_ipmap *) set->data; ++ ++ kfree(map->members); ++ kfree(map); ++ ++ set->data = NULL; ++} ++ ++static void flush(struct ip_set *set) ++{ ++ struct ip_set_ipmap *map = (struct ip_set_ipmap *) set->data; ++ memset(map->members, 0, bitmap_bytes(0, map->sizeid - 1)); ++} ++ ++static void list_header(const struct ip_set *set, void *data) ++{ ++ struct ip_set_ipmap *map = (struct ip_set_ipmap *) set->data; ++ struct ip_set_req_ipmap_create *header = ++ (struct ip_set_req_ipmap_create *) data; ++ ++ header->from = map->first_ip; ++ header->to = map->last_ip; ++ header->netmask = map->netmask; ++} ++ ++static int list_members_size(const struct ip_set *set) ++{ ++ struct ip_set_ipmap *map = (struct ip_set_ipmap *) set->data; ++ ++ return bitmap_bytes(0, map->sizeid - 1); ++} ++ ++static void list_members(const struct ip_set *set, void *data) ++{ ++ struct ip_set_ipmap *map = (struct ip_set_ipmap *) set->data; ++ int bytes = bitmap_bytes(0, map->sizeid - 1); ++ ++ memcpy(data, map->members, bytes); ++} ++ ++static struct ip_set_type ip_set_ipmap = { ++ .typename = SETTYPE_NAME, ++ .typecode = IPSET_TYPE_IP, ++ .protocol_version = IP_SET_PROTOCOL_VERSION, ++ .create = &create, ++ .destroy = &destroy, ++ .flush = &flush, ++ .reqsize = sizeof(struct ip_set_req_ipmap), ++ .addip = &addip, ++ .addip_kernel = &addip_kernel, ++ .delip = &delip, ++ .delip_kernel = &delip_kernel, ++ .testip = &testip, ++ .testip_kernel = &testip_kernel, ++ .header_size = sizeof(struct ip_set_req_ipmap_create), ++ .list_header = &list_header, ++ .list_members_size = &list_members_size, ++ .list_members = &list_members, ++ .me = THIS_MODULE, ++}; ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>"); ++MODULE_DESCRIPTION("ipmap type of IP sets"); ++ ++static int __init init(void) ++{ ++ return ip_set_register_set_type(&ip_set_ipmap); ++} ++ ++static void __exit fini(void) ++{ ++ /* FIXME: possible race with ip_set_create() */ ++ ip_set_unregister_set_type(&ip_set_ipmap); ++} ++ ++module_init(init); ++module_exit(fini); +diff -Naur linux-old/net/ipv4/netfilter/ip_set_iptree.c linux-new/net/ipv4/netfilter/ip_set_iptree.c +--- linux-old/net/ipv4/netfilter/ip_set_iptree.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-new/net/ipv4/netfilter/ip_set_iptree.c 2005-08-10 22:13:00.000000000 +0200 +@@ -0,0 +1,511 @@ ++/* Copyright (C) 2005 Jozsef Kadlecsik <kadlec@blackhole.kfki.hu> ++ * ++ * 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. ++ */ ++ ++/* Kernel module implementing an IP set type: the iptree type */ ++ ++#include <linux/module.h> ++#include <linux/ip.h> ++#include <linux/skbuff.h> ++#include <linux/slab.h> ++#include <linux/delay.h> ++#include <linux/netfilter_ipv4/ip_tables.h> ++#include <linux/netfilter_ipv4/ip_set.h> ++#include <linux/errno.h> ++#include <asm/uaccess.h> ++#include <asm/bitops.h> ++#include <linux/interrupt.h> ++#include <linux/spinlock.h> ++ ++#include <linux/netfilter_ipv4/ip_set_iptree.h> ++ ++/* Garbage collection interval in seconds: */ ++#define IPTREE_GC_TIME 5*60 ++/* Sleep so many milliseconds before trying again ++ * to delete the gc timer at destroying a set */ ++#define IPTREE_DESTROY_SLEEP 100 ++ ++static kmem_cache_t *branch_cachep; ++static kmem_cache_t *leaf_cachep; ++ ++#define ABCD(a,b,c,d,addrp) do { \ ++ a = ((unsigned char *)addrp)[3]; \ ++ b = ((unsigned char *)addrp)[2]; \ ++ c = ((unsigned char *)addrp)[1]; \ ++ d = ((unsigned char *)addrp)[0]; \ ++} while (0) ++ ++#define TESTIP_WALK(map, elem, branch) do { \ ++ if ((map)->tree[elem]) { \ ++ branch = (map)->tree[elem]; \ ++ } else \ ++ return 0; \ ++} while (0) ++ ++static inline int ++__testip(struct ip_set *set, ip_set_ip_t ip, ip_set_ip_t *hash_ip) ++{ ++ struct ip_set_iptree *map = (struct ip_set_iptree *) set->data; ++ struct ip_set_iptreeb *btree; ++ struct ip_set_iptreec *ctree; ++ struct ip_set_iptreed *dtree; ++ unsigned char a,b,c,d; ++ ++ *hash_ip = ip; ++ ABCD(a, b, c, d, hash_ip); ++ DP("%u %u %u %u timeout %u", a, b, c, d, map->timeout); ++ TESTIP_WALK(map, a, btree); ++ TESTIP_WALK(btree, b, ctree); ++ TESTIP_WALK(ctree, c, dtree); ++ DP("%lu %lu", dtree->expires[d], jiffies); ++ return !!(map->timeout ? (time_after(dtree->expires[d], jiffies)) ++ : dtree->expires[d]); ++} ++ ++static int ++testip(struct ip_set *set, const void *data, size_t size, ++ ip_set_ip_t *hash_ip) ++{ ++ struct ip_set_req_iptree *req = ++ (struct ip_set_req_iptree *) data; ++ ++ if (size != sizeof(struct ip_set_req_iptree)) { ++ ip_set_printk("data length wrong (want %zu, have %zu)", ++ sizeof(struct ip_set_req_iptree), ++ size); ++ return -EINVAL; ++ } ++ return __testip(set, req->ip, hash_ip); ++} ++ ++static int ++testip_kernel(struct ip_set *set, ++ const struct sk_buff *skb, ++ u_int32_t flags, ++ ip_set_ip_t *hash_ip) ++{ ++ int res; ++ ++ DP("flag: %s src: %u.%u.%u.%u dst: %u.%u.%u.%u", ++ flags & IPSET_SRC ? "SRC" : "DST", ++ NIPQUAD(skb->nh.iph->saddr), ++ NIPQUAD(skb->nh.iph->daddr)); ++ ++ res = __testip(set, ++ ntohl(flags & IPSET_SRC ? skb->nh.iph->saddr ++ : skb->nh.iph->daddr), ++ hash_ip); ++ return (res < 0 ? 0 : res); ++} ++ ++#define ADDIP_WALK(map, elem, branch, type, cachep) do { \ ++ if ((map)->tree[elem]) { \ ++ DP("found %u", elem); \ ++ branch = (map)->tree[elem]; \ ++ } else { \ ++ branch = (type *) \ ++ kmem_cache_alloc(cachep, GFP_KERNEL); \ ++ if (branch == NULL) \ ++ return -ENOMEM; \ ++ memset(branch, 0, sizeof(*branch)); \ ++ (map)->tree[elem] = branch; \ ++ DP("alloc %u", elem); \ ++ } \ ++} while (0) ++ ++static inline int ++__addip(struct ip_set *set, ip_set_ip_t ip, unsigned int timeout, ++ ip_set_ip_t *hash_ip) ++{ ++ struct ip_set_iptree *map = (struct ip_set_iptree *) set->data; ++ struct ip_set_iptreeb *btree; ++ struct ip_set_iptreec *ctree; ++ struct ip_set_iptreed *dtree; ++ unsigned char a,b,c,d; ++ int ret = 0; ++ ++ *hash_ip = ip; ++ ABCD(a, b, c, d, hash_ip); ++ DP("%u %u %u %u timeout %u", a, b, c, d, timeout); ++ ADDIP_WALK(map, a, btree, struct ip_set_iptreeb, branch_cachep); ++ ADDIP_WALK(btree, b, ctree, struct ip_set_iptreec, branch_cachep); ++ ADDIP_WALK(ctree, c, dtree, struct ip_set_iptreed, leaf_cachep); ++ if (dtree->expires[d] ++ && (!map->timeout || time_after(dtree->expires[d], jiffies))) ++ ret = -EEXIST; ++ dtree->expires[d] = map->timeout ? (timeout * HZ + jiffies) : 1; ++ DP("%u %lu", d, dtree->expires[d]); ++ return ret; ++} ++ ++static int ++addip(struct ip_set *set, const void *data, size_t size, ++ ip_set_ip_t *hash_ip) ++{ ++ struct ip_set_iptree *map = (struct ip_set_iptree *) set->data; ++ struct ip_set_req_iptree *req = ++ (struct ip_set_req_iptree *) data; ++ ++ if (size != sizeof(struct ip_set_req_iptree)) { ++ ip_set_printk("data length wrong (want %zu, have %zu)", ++ sizeof(struct ip_set_req_iptree), ++ size); ++ return -EINVAL; ++ } ++ DP("%u.%u.%u.%u %u", HIPQUAD(req->ip), req->timeout); ++ return __addip(set, req->ip, ++ req->timeout ? req->timeout : map->timeout, ++ hash_ip); ++} ++ ++static int ++addip_kernel(struct ip_set *set, const struct sk_buff *skb, ++ u_int32_t flags, ip_set_ip_t *hash_ip) ++{ ++ struct ip_set_iptree *map = (struct ip_set_iptree *) set->data; ++ ++ return __addip(set, ++ ntohl(flags & IPSET_SRC ? skb->nh.iph->saddr ++ : skb->nh.iph->daddr), ++ map->timeout, ++ hash_ip); ++} ++ ++#define DELIP_WALK(map, elem, branch) do { \ ++ if ((map)->tree[elem]) { \ ++ branch = (map)->tree[elem]; \ ++ } else \ ++ return -EEXIST; \ ++} while (0) ++ ++static inline int ++__delip(struct ip_set *set, ip_set_ip_t ip, ip_set_ip_t *hash_ip) ++{ ++ struct ip_set_iptree *map = (struct ip_set_iptree *) set->data; ++ struct ip_set_iptreeb *btree; ++ struct ip_set_iptreec *ctree; ++ struct ip_set_iptreed *dtree; ++ unsigned char a,b,c,d; ++ ++ *hash_ip = ip; ++ ABCD(a, b, c, d, hash_ip); ++ DELIP_WALK(map, a, btree); ++ DELIP_WALK(btree, b, ctree); ++ DELIP_WALK(ctree, c, dtree); ++ ++ if (dtree->expires[d]) { ++ dtree->expires[d] = 0; ++ return 0; ++ } ++ return -EEXIST; ++} ++ ++static int ++delip(struct ip_set *set, const void *data, size_t size, ++ ip_set_ip_t *hash_ip) ++{ ++ struct ip_set_req_iptree *req = ++ (struct ip_set_req_iptree *) data; ++ ++ if (size != sizeof(struct ip_set_req_iptree)) { ++ ip_set_printk("data length wrong (want %zu, have %zu)", ++ sizeof(struct ip_set_req_iptree), ++ size); ++ return -EINVAL; ++ } ++ return __delip(set, req->ip, hash_ip); ++} ++ ++static int ++delip_kernel(struct ip_set *set, const struct sk_buff *skb, ++ u_int32_t flags, ip_set_ip_t *hash_ip) ++{ ++ return __delip(set, ++ ntohl(flags & IPSET_SRC ? skb->nh.iph->saddr ++ : skb->nh.iph->daddr), ++ hash_ip); ++} ++ ++#define LOOP_WALK_BEGIN(map, i, branch) \ ++ for (i = 0; i < 255; i++) { \ ++ if (!(map)->tree[i]) \ ++ continue; \ ++ branch = (map)->tree[i] ++ ++#define LOOP_WALK_END } ++ ++static void ip_tree_gc(unsigned long ul_set) ++{ ++ struct ip_set *set = (void *) ul_set; ++ struct ip_set_iptree *map = (struct ip_set_iptree *) set->data; ++ struct ip_set_iptreeb *btree; ++ struct ip_set_iptreec *ctree; ++ struct ip_set_iptreed *dtree; ++ unsigned char a,b,c,d; ++ unsigned char i,j,k; ++ ++ i = j = k = 0; ++ DP("gc: %s", set->name); ++ write_lock_bh(&set->lock); ++ LOOP_WALK_BEGIN(map, a, btree); ++ LOOP_WALK_BEGIN(btree, b, ctree); ++ LOOP_WALK_BEGIN(ctree, c, dtree); ++ for (d = 0; d < 255; d++) { ++ if (dtree->expires[d]) { ++ DP("gc: %u %u %u %u: expires %lu jiffies %lu", ++ a, b, c, d, ++ dtree->expires[d], jiffies); ++ if (map->timeout ++ && time_before(dtree->expires[d], jiffies)) ++ dtree->expires[d] = 0; ++ else ++ k = 1; ++ } ++ } ++ if (k == 0) { ++ DP("gc: %s: leaf %u %u %u empty", ++ set->name, a, b, c); ++ kmem_cache_free(leaf_cachep, dtree); ++ ctree->tree[c] = NULL; ++ } else { ++ DP("gc: %s: leaf %u %u %u not empty", ++ set->name, a, b, c); ++ j = 1; ++ k = 0; ++ } ++ LOOP_WALK_END; ++ if (j == 0) { ++ DP("gc: %s: branch %u %u empty", ++ set->name, a, b); ++ kmem_cache_free(branch_cachep, ctree); ++ btree->tree[b] = NULL; ++ } else { ++ DP("gc: %s: branch %u %u not empty", ++ set->name, a, b); ++ i = 1; ++ j = k = 0; ++ } ++ LOOP_WALK_END; ++ if (i == 0) { ++ DP("gc: %s: branch %u empty", ++ set->name, a); ++ kmem_cache_free(branch_cachep, btree); ++ map->tree[a] = NULL; ++ } else { ++ DP("gc: %s: branch %u not empty", ++ set->name, a); ++ i = j = k = 0; ++ } ++ LOOP_WALK_END; ++ write_unlock_bh(&set->lock); ++ ++ map->gc.expires = jiffies + map->gc_interval * HZ; ++ add_timer(&map->gc); ++} ++ ++static int create(struct ip_set *set, const void *data, size_t size) ++{ ++ struct ip_set_req_iptree_create *req = ++ (struct ip_set_req_iptree_create *) data; ++ struct ip_set_iptree *map; ++ ++ if (size != sizeof(struct ip_set_req_iptree_create)) { ++ ip_set_printk("data length wrong (want %zu, have %zu)", ++ sizeof(struct ip_set_req_iptree_create), ++ size); ++ return -EINVAL; ++ } ++ ++ map = kmalloc(sizeof(struct ip_set_iptree), GFP_KERNEL); ++ if (!map) { ++ DP("out of memory for %d bytes", ++ sizeof(struct ip_set_iptree)); ++ return -ENOMEM; ++ } ++ memset(map, 0, sizeof(*map)); ++ map->timeout = req->timeout; ++ set->data = map; ++ ++ /* If there is no timeout for the entries, ++ * we still have to call gc because delete ++ * do not clean up empty branches */ ++ map->gc_interval = IPTREE_GC_TIME; ++ init_timer(&map->gc); ++ map->gc.data = (unsigned long) set; ++ map->gc.function = ip_tree_gc; ++ map->gc.expires = jiffies + map->gc_interval * HZ; ++ add_timer(&map->gc); ++ ++ return 0; ++} ++ ++static void __flush(struct ip_set_iptree *map) ++{ ++ struct ip_set_iptreeb *btree; ++ struct ip_set_iptreec *ctree; ++ struct ip_set_iptreed *dtree; ++ unsigned int a,b,c; ++ ++ LOOP_WALK_BEGIN(map, a, btree); ++ LOOP_WALK_BEGIN(btree, b, ctree); ++ LOOP_WALK_BEGIN(ctree, c, dtree); ++ kmem_cache_free(leaf_cachep, dtree); ++ LOOP_WALK_END; ++ kmem_cache_free(branch_cachep, ctree); ++ LOOP_WALK_END; ++ kmem_cache_free(branch_cachep, btree); ++ LOOP_WALK_END; ++} ++ ++static void destroy(struct ip_set *set) ++{ ++ struct ip_set_iptree *map = (struct ip_set_iptree *) set->data; ++ ++ while (!del_timer(&map->gc)) ++ msleep(IPTREE_DESTROY_SLEEP); ++ __flush(map); ++ kfree(map); ++ set->data = NULL; ++} ++ ++static void flush(struct ip_set *set) ++{ ++ struct ip_set_iptree *map = (struct ip_set_iptree *) set->data; ++ unsigned int timeout = map->timeout; ++ ++ __flush(map); ++ memset(map, 0, sizeof(*map)); ++ map->timeout = timeout; ++} ++ ++static void list_header(const struct ip_set *set, void *data) ++{ ++ struct ip_set_iptree *map = (struct ip_set_iptree *) set->data; ++ struct ip_set_req_iptree_create *header = ++ (struct ip_set_req_iptree_create *) data; ++ ++ header->timeout = map->timeout; ++} ++ ++static int list_members_size(const struct ip_set *set) ++{ ++ struct ip_set_iptree *map = (struct ip_set_iptree *) set->data; ++ struct ip_set_iptreeb *btree; ++ struct ip_set_iptreec *ctree; ++ struct ip_set_iptreed *dtree; ++ unsigned char a,b,c,d; ++ unsigned int count = 0; ++ ++ LOOP_WALK_BEGIN(map, a, btree); ++ LOOP_WALK_BEGIN(btree, b, ctree); ++ LOOP_WALK_BEGIN(ctree, c, dtree); ++ for (d = 0; d < 255; d++) { ++ if (dtree->expires[d] ++ && (!map->timeout || time_after(dtree->expires[d], jiffies))) ++ count++; ++ } ++ LOOP_WALK_END; ++ LOOP_WALK_END; ++ LOOP_WALK_END; ++ ++ DP("members %u", count); ++ return (count * sizeof(struct ip_set_req_iptree)); ++} ++ ++static void list_members(const struct ip_set *set, void *data) ++{ ++ struct ip_set_iptree *map = (struct ip_set_iptree *) set->data; ++ struct ip_set_iptreeb *btree; ++ struct ip_set_iptreec *ctree; ++ struct ip_set_iptreed *dtree; ++ unsigned char a,b,c,d; ++ size_t offset = 0; ++ struct ip_set_req_iptree *entry; ++ ++ LOOP_WALK_BEGIN(map, a, btree); ++ LOOP_WALK_BEGIN(btree, b, ctree); ++ LOOP_WALK_BEGIN(ctree, c, dtree); ++ for (d = 0; d < 255; d++) { ++ if (dtree->expires[d] ++ && (!map->timeout || time_after(dtree->expires[d], jiffies))) { ++ entry = (struct ip_set_req_iptree *)(data + offset); ++ entry->ip = ((a << 24) | (b << 16) | (c << 8) | d); ++ entry->timeout = !map->timeout ? 0 ++ : (dtree->expires[d] - jiffies)/HZ; ++ offset += sizeof(struct ip_set_req_iptree); ++ } ++ } ++ LOOP_WALK_END; ++ LOOP_WALK_END; ++ LOOP_WALK_END; ++} ++ ++static struct ip_set_type ip_set_iptree = { ++ .typename = SETTYPE_NAME, ++ .typecode = IPSET_TYPE_IP, ++ .protocol_version = IP_SET_PROTOCOL_VERSION, ++ .create = &create, ++ .destroy = &destroy, ++ .flush = &flush, ++ .reqsize = sizeof(struct ip_set_req_iptree), ++ .addip = &addip, ++ .addip_kernel = &addip_kernel, ++ .delip = &delip, ++ .delip_kernel = &delip_kernel, ++ .testip = &testip, ++ .testip_kernel = &testip_kernel, ++ .header_size = sizeof(struct ip_set_req_iptree_create), ++ .list_header = &list_header, ++ .list_members_size = &list_members_size, ++ .list_members = &list_members, ++ .me = THIS_MODULE, ++}; ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>"); ++MODULE_DESCRIPTION("iptree type of IP sets"); ++ ++static int __init init(void) ++{ ++ int ret; ++ ++ branch_cachep = kmem_cache_create("ip_set_iptreeb", ++ sizeof(struct ip_set_iptreeb), ++ 0, 0, NULL, NULL); ++ if (!branch_cachep) { ++ printk(KERN_ERR "Unable to create ip_set_iptreeb slab cache\n"); ++ ret = -ENOMEM; ++ goto out; ++ } ++ leaf_cachep = kmem_cache_create("ip_set_iptreed", ++ sizeof(struct ip_set_iptreed), ++ 0, 0, NULL, NULL); ++ if (!leaf_cachep) { ++ printk(KERN_ERR "Unable to create ip_set_iptreed slab cache\n"); ++ ret = -ENOMEM; ++ goto free_branch; ++ } ++ ret = ip_set_register_set_type(&ip_set_iptree); ++ if (ret == 0) ++ goto out; ++ ++ kmem_cache_destroy(leaf_cachep); ++ free_branch: ++ kmem_cache_destroy(branch_cachep); ++ out: ++ return ret; ++} ++ ++static void __exit fini(void) ++{ ++ /* FIXME: possible race with ip_set_create() */ ++ ip_set_unregister_set_type(&ip_set_iptree); ++ kmem_cache_destroy(leaf_cachep); ++ kmem_cache_destroy(branch_cachep); ++} ++ ++module_init(init); ++module_exit(fini); +diff -Naur linux-old/net/ipv4/netfilter/ip_set_macipmap.c linux-new/net/ipv4/netfilter/ip_set_macipmap.c +--- linux-old/net/ipv4/netfilter/ip_set_macipmap.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-new/net/ipv4/netfilter/ip_set_macipmap.c 2005-08-10 22:13:00.000000000 +0200 +@@ -0,0 +1,340 @@ ++/* Copyright (C) 2000-2002 Joakim Axelsson <gozem@linux.nu> ++ * Patrick Schaaf <bof@bof.de> ++ * Martin Josefsson <gandalf@wlug.westbo.se> ++ * Copyright (C) 2003-2004 Jozsef Kadlecsik <kadlec@blackhole.kfki.hu> ++ * ++ * 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. ++ */ ++ ++/* Kernel module implementing an IP set type: the macipmap type */ ++ ++#include <linux/module.h> ++#include <linux/ip.h> ++#include <linux/skbuff.h> ++#include <linux/netfilter_ipv4/ip_tables.h> ++#include <linux/netfilter_ipv4/ip_set.h> ++#include <linux/errno.h> ++#include <asm/uaccess.h> ++#include <asm/bitops.h> ++#include <asm/softirq.h> ++#include <linux/spinlock.h> ++#include <linux/if_ether.h> ++#include <linux/vmalloc.h> ++ ++#include <linux/netfilter_ipv4/ip_set_malloc.h> ++#include <linux/netfilter_ipv4/ip_set_macipmap.h> ++ ++static int ++testip(struct ip_set *set, const void *data, size_t size, ip_set_ip_t *hash_ip) ++{ ++ struct ip_set_macipmap *map = (struct ip_set_macipmap *) set->data; ++ struct ip_set_macip *table = (struct ip_set_macip *) map->members; ++ struct ip_set_req_macipmap *req = (struct ip_set_req_macipmap *) data; ++ ++ if (size != sizeof(struct ip_set_req_macipmap)) { ++ ip_set_printk("data length wrong (want %zu, have %zu)", ++ sizeof(struct ip_set_req_macipmap), ++ size); ++ return -EINVAL; ++ } ++ ++ if (req->ip < map->first_ip || req->ip > map->last_ip) ++ return -ERANGE; ++ ++ *hash_ip = req->ip; ++ DP("set: %s, ip:%u.%u.%u.%u, %u.%u.%u.%u", ++ set->name, HIPQUAD(req->ip), HIPQUAD(*hash_ip)); ++ if (test_bit(IPSET_MACIP_ISSET, ++ &table[req->ip - map->first_ip].flags)) { ++ return (memcmp(req->ethernet, ++ &table[req->ip - map->first_ip].ethernet, ++ ETH_ALEN) == 0); ++ } else { ++ return (map->flags & IPSET_MACIP_MATCHUNSET ? 1 : 0); ++ } ++} ++ ++static int ++testip_kernel(struct ip_set *set, const struct sk_buff *skb, ++ u_int32_t flags, ip_set_ip_t *hash_ip) ++{ ++ struct ip_set_macipmap *map = ++ (struct ip_set_macipmap *) set->data; ++ struct ip_set_macip *table = ++ (struct ip_set_macip *) map->members; ++ ip_set_ip_t ip; ++ ++ ip = ntohl(flags & IPSET_SRC ? skb->nh.iph->saddr ++ : skb->nh.iph->daddr); ++ DP("flag: %s src: %u.%u.%u.%u dst: %u.%u.%u.%u", ++ flags & IPSET_SRC ? "SRC" : "DST", ++ NIPQUAD(skb->nh.iph->saddr), ++ NIPQUAD(skb->nh.iph->daddr)); ++ ++ if (ip < map->first_ip || ip > map->last_ip) ++ return 0; ++ ++ *hash_ip = ip; ++ DP("set: %s, ip:%u.%u.%u.%u, %u.%u.%u.%u", ++ set->name, HIPQUAD(ip), HIPQUAD(*hash_ip)); ++ if (test_bit(IPSET_MACIP_ISSET, &table[ip - map->first_ip].flags)) { ++ /* Is mac pointer valid? ++ * If so, compare... */ ++ return (skb->mac.raw >= skb->head ++ && (skb->mac.raw + ETH_HLEN) <= skb->data ++ && (memcmp(skb->mac.ethernet->h_source, ++ &table[ip - map->first_ip].ethernet, ++ ETH_ALEN) == 0)); ++ } else { ++ return (map->flags & IPSET_MACIP_MATCHUNSET ? 1 : 0); ++ } ++} ++ ++/* returns 0 on success */ ++static inline int ++__addip(struct ip_set *set, ++ ip_set_ip_t ip, unsigned char *ethernet, ip_set_ip_t *hash_ip) ++{ ++ struct ip_set_macipmap *map = ++ (struct ip_set_macipmap *) set->data; ++ struct ip_set_macip *table = ++ (struct ip_set_macip *) map->members; ++ ++ if (ip < map->first_ip || ip > map->last_ip) ++ return -ERANGE; ++ if (test_and_set_bit(IPSET_MACIP_ISSET, ++ &table[ip - map->first_ip].flags)) ++ return -EEXIST; ++ ++ *hash_ip = ip; ++ DP("%u.%u.%u.%u, %u.%u.%u.%u", HIPQUAD(ip), HIPQUAD(*hash_ip)); ++ memcpy(&table[ip - map->first_ip].ethernet, ethernet, ETH_ALEN); ++ return 0; ++} ++ ++static int ++addip(struct ip_set *set, const void *data, size_t size, ++ ip_set_ip_t *hash_ip) ++{ ++ struct ip_set_req_macipmap *req = ++ (struct ip_set_req_macipmap *) data; ++ ++ if (size != sizeof(struct ip_set_req_macipmap)) { ++ ip_set_printk("data length wrong (want %zu, have %zu)", ++ sizeof(struct ip_set_req_macipmap), ++ size); ++ return -EINVAL; ++ } ++ return __addip(set, req->ip, req->ethernet, hash_ip); ++} ++ ++static int ++addip_kernel(struct ip_set *set, const struct sk_buff *skb, ++ u_int32_t flags, ip_set_ip_t *hash_ip) ++{ ++ ip_set_ip_t ip; ++ ++ ip = ntohl(flags & IPSET_SRC ? skb->nh.iph->saddr ++ : skb->nh.iph->daddr); ++ ++ if (!(skb->mac.raw >= skb->head ++ && (skb->mac.raw + ETH_HLEN) <= skb->data)) ++ return -EINVAL; ++ ++ return __addip(set, ip, skb->mac.ethernet->h_source, hash_ip); ++} ++ ++static inline int ++__delip(struct ip_set *set, ip_set_ip_t ip, ip_set_ip_t *hash_ip) ++{ ++ struct ip_set_macipmap *map = ++ (struct ip_set_macipmap *) set->data; ++ struct ip_set_macip *table = ++ (struct ip_set_macip *) map->members; ++ ++ if (ip < map->first_ip || ip > map->last_ip) ++ return -ERANGE; ++ if (!test_and_clear_bit(IPSET_MACIP_ISSET, ++ &table[ip - map->first_ip].flags)) ++ return -EEXIST; ++ ++ *hash_ip = ip; ++ DP("%u.%u.%u.%u, %u.%u.%u.%u", HIPQUAD(ip), HIPQUAD(*hash_ip)); ++ return 0; ++} ++ ++static int ++delip(struct ip_set *set, const void *data, size_t size, ++ ip_set_ip_t *hash_ip) ++{ ++ struct ip_set_req_macipmap *req = ++ (struct ip_set_req_macipmap *) data; ++ ++ if (size != sizeof(struct ip_set_req_macipmap)) { ++ ip_set_printk("data length wrong (want %zu, have %zu)", ++ sizeof(struct ip_set_req_macipmap), ++ size); ++ return -EINVAL; ++ } ++ return __delip(set, req->ip, hash_ip); ++} ++ ++static int ++delip_kernel(struct ip_set *set, const struct sk_buff *skb, ++ u_int32_t flags, ip_set_ip_t *hash_ip) ++{ ++ return __delip(set, ++ ntohl(flags & IPSET_SRC ? skb->nh.iph->saddr ++ : skb->nh.iph->daddr), ++ hash_ip); ++} ++ ++static inline size_t members_size(ip_set_id_t from, ip_set_id_t to) ++{ ++ return (size_t)((to - from + 1) * sizeof(struct ip_set_macip)); ++} ++ ++static int create(struct ip_set *set, const void *data, size_t size) ++{ ++ int newbytes; ++ struct ip_set_req_macipmap_create *req = ++ (struct ip_set_req_macipmap_create *) data; ++ struct ip_set_macipmap *map; ++ ++ if (size != sizeof(struct ip_set_req_macipmap_create)) { ++ ip_set_printk("data length wrong (want %zu, have %zu)", ++ sizeof(struct ip_set_req_macipmap_create), ++ size); ++ return -EINVAL; ++ } ++ ++ DP("from %u.%u.%u.%u to %u.%u.%u.%u", ++ HIPQUAD(req->from), HIPQUAD(req->to)); ++ ++ if (req->from > req->to) { ++ DP("bad ip range"); ++ return -ENOEXEC; ++ } ++ ++ if (req->to - req->from > MAX_RANGE) { ++ ip_set_printk("range too big (max %d addresses)", ++ MAX_RANGE); ++ return -ENOEXEC; ++ } ++ ++ map = kmalloc(sizeof(struct ip_set_macipmap), GFP_KERNEL); ++ if (!map) { ++ DP("out of memory for %d bytes", ++ sizeof(struct ip_set_macipmap)); ++ return -ENOMEM; ++ } ++ map->flags = req->flags; ++ map->first_ip = req->from; ++ map->last_ip = req->to; ++ newbytes = members_size(map->first_ip, map->last_ip); ++ map->members = ip_set_malloc(newbytes); ++ DP("members: %u %p", newbytes, map->members); ++ if (!map->members) { ++ DP("out of memory for %d bytes", newbytes); ++ kfree(map); ++ return -ENOMEM; ++ } ++ memset(map->members, 0, newbytes); ++ ++ set->data = map; ++ return 0; ++} ++ ++static void destroy(struct ip_set *set) ++{ ++ struct ip_set_macipmap *map = ++ (struct ip_set_macipmap *) set->data; ++ ++ ip_set_free(map->members, members_size(map->first_ip, map->last_ip)); ++ kfree(map); ++ ++ set->data = NULL; ++} ++ ++static void flush(struct ip_set *set) ++{ ++ struct ip_set_macipmap *map = ++ (struct ip_set_macipmap *) set->data; ++ memset(map->members, 0, members_size(map->first_ip, map->last_ip)); ++} ++ ++static void list_header(const struct ip_set *set, void *data) ++{ ++ struct ip_set_macipmap *map = ++ (struct ip_set_macipmap *) set->data; ++ struct ip_set_req_macipmap_create *header = ++ (struct ip_set_req_macipmap_create *) data; ++ ++ DP("list_header %x %x %u", map->first_ip, map->last_ip, ++ map->flags); ++ ++ header->from = map->first_ip; ++ header->to = map->last_ip; ++ header->flags = map->flags; ++} ++ ++static int list_members_size(const struct ip_set *set) ++{ ++ struct ip_set_macipmap *map = ++ (struct ip_set_macipmap *) set->data; ++ ++ DP("%u", members_size(map->first_ip, map->last_ip)); ++ return members_size(map->first_ip, map->last_ip); ++} ++ ++static void list_members(const struct ip_set *set, void *data) ++{ ++ struct ip_set_macipmap *map = ++ (struct ip_set_macipmap *) set->data; ++ ++ int bytes = members_size(map->first_ip, map->last_ip); ++ ++ DP("members: %u %p", bytes, map->members); ++ memcpy(data, map->members, bytes); ++} ++ ++static struct ip_set_type ip_set_macipmap = { ++ .typename = SETTYPE_NAME, ++ .typecode = IPSET_TYPE_IP, ++ .protocol_version = IP_SET_PROTOCOL_VERSION, ++ .create = &create, ++ .destroy = &destroy, ++ .flush = &flush, ++ .reqsize = sizeof(struct ip_set_req_macipmap), ++ .addip = &addip, ++ .addip_kernel = &addip_kernel, ++ .delip = &delip, ++ .delip_kernel = &delip_kernel, ++ .testip = &testip, ++ .testip_kernel = &testip_kernel, ++ .header_size = sizeof(struct ip_set_req_macipmap_create), ++ .list_header = &list_header, ++ .list_members_size = &list_members_size, ++ .list_members = &list_members, ++ .me = THIS_MODULE, ++}; ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>"); ++MODULE_DESCRIPTION("macipmap type of IP sets"); ++ ++static int __init init(void) ++{ ++ return ip_set_register_set_type(&ip_set_macipmap); ++} ++ ++static void __exit fini(void) ++{ ++ /* FIXME: possible race with ip_set_create() */ ++ ip_set_unregister_set_type(&ip_set_macipmap); ++} ++ ++module_init(init); ++module_exit(fini); +diff -Naur linux-old/net/ipv4/netfilter/ip_set_nethash.c linux-new/net/ipv4/netfilter/ip_set_nethash.c +--- linux-old/net/ipv4/netfilter/ip_set_nethash.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-new/net/ipv4/netfilter/ip_set_nethash.c 2005-08-10 22:13:00.000000000 +0200 +@@ -0,0 +1,450 @@ ++/* Copyright (C) 2003-2004 Jozsef Kadlecsik <kadlec@blackhole.kfki.hu> ++ * ++ * 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. ++ */ ++ ++/* Kernel module implementing a cidr nethash set */ ++ ++#include <linux/module.h> ++#include <linux/ip.h> ++#include <linux/skbuff.h> ++#include <linux/netfilter_ipv4/ip_tables.h> ++#include <linux/netfilter_ipv4/ip_set.h> ++#include <linux/errno.h> ++#include <asm/uaccess.h> ++#include <asm/bitops.h> ++#include <asm/softirq.h> ++#include <linux/spinlock.h> ++#include <linux/vmalloc.h> ++#include <linux/random.h> ++ ++#include <net/ip.h> ++ ++#include <linux/netfilter_ipv4/ip_set_malloc.h> ++#include <linux/netfilter_ipv4/ip_set_nethash.h> ++#include <linux/netfilter_ipv4/ip_set_jhash.h> ++#include <linux/netfilter_ipv4/ip_set_prime.h> ++ ++static inline __u32 ++jhash_ip(const struct ip_set_nethash *map, ip_set_ip_t ip) ++{ ++ return jhash_1word(ip, map->initval); ++} ++ ++static inline __u32 ++randhash_ip(const struct ip_set_nethash *map, ip_set_ip_t ip) ++{ ++ return (1 + ip % map->prime); ++} ++ ++static inline __u32 ++hash_id_cidr(struct ip_set_nethash *map, ++ ip_set_ip_t ip, ++ unsigned char cidr, ++ ip_set_ip_t *hash_ip) ++{ ++ __u32 jhash, randhash, id; ++ u_int16_t i; ++ ++ *hash_ip = pack(ip, cidr); ++ jhash = jhash_ip(map, *hash_ip); ++ randhash = randhash_ip(map, *hash_ip); ++ ++ for (i = 0; i < map->probes; i++) { ++ id = (jhash + i * randhash) % map->hashsize; ++ DP("hash key: %u", id); ++ if (map->members[id] == *hash_ip) ++ return id; ++ } ++ return UINT_MAX; ++} ++ ++static inline __u32 ++hash_id(struct ip_set *set, ip_set_ip_t ip, ip_set_ip_t *hash_ip) ++{ ++ struct ip_set_nethash *map = (struct ip_set_nethash *) set->data; ++ __u32 id = UINT_MAX; ++ int i; ++ ++ for (i = 0; i < 30 && map->cidr[i]; i++) { ++ id = hash_id_cidr(map, ip, map->cidr[i], hash_ip); ++ if (id != UINT_MAX) ++ break; ++ } ++ return id; ++} ++ ++static inline int ++__testip_cidr(struct ip_set *set, ip_set_ip_t ip, unsigned char cidr, ++ ip_set_ip_t *hash_ip) ++{ ++ struct ip_set_nethash *map = (struct ip_set_nethash *) set->data; ++ ++ return (hash_id_cidr(map, ip, cidr, hash_ip) != UINT_MAX); ++} ++ ++static inline int ++__testip(struct ip_set *set, ip_set_ip_t ip, ip_set_ip_t *hash_ip) ++{ ++ return (hash_id(set, ip, hash_ip) != UINT_MAX); ++} ++ ++static int ++testip(struct ip_set *set, const void *data, size_t size, ++ ip_set_ip_t *hash_ip) ++{ ++ struct ip_set_req_nethash *req = ++ (struct ip_set_req_nethash *) data; ++ ++ if (size != sizeof(struct ip_set_req_nethash)) { ++ ip_set_printk("data length wrong (want %zu, have %zu)", ++ sizeof(struct ip_set_req_nethash), ++ size); ++ return -EINVAL; ++ } ++ return (req->cidr == 32 ? __testip(set, req->ip, hash_ip) ++ : __testip_cidr(set, req->ip, req->cidr, hash_ip)); ++} ++ ++static int ++testip_kernel(struct ip_set *set, const struct sk_buff *skb, ++ u_int32_t flags, ip_set_ip_t *hash_ip) ++{ ++ return __testip(set, ++ ntohl(flags & IPSET_SRC ? skb->nh.iph->saddr ++ : skb->nh.iph->daddr), ++ hash_ip); ++} ++ ++static inline int ++__addip_base(struct ip_set_nethash *map, ip_set_ip_t ip) ++{ ++ __u32 jhash, randhash, probe; ++ u_int16_t i; ++ ++ jhash = jhash_ip(map, ip); ++ randhash = randhash_ip(map, ip); ++ ++ for (i = 0; i < map->probes; i++) { ++ probe = (jhash + i * randhash) % map->hashsize; ++ if (map->members[probe] == ip) ++ return -EEXIST; ++ if (!map->members[probe]) { ++ map->members[probe] = ip; ++ return 0; ++ } ++ } ++ /* Trigger rehashing */ ++ return -EAGAIN; ++} ++ ++static inline int ++__addip(struct ip_set_nethash *map, ip_set_ip_t ip, unsigned char cidr, ++ ip_set_ip_t *hash_ip) ++{ ++ *hash_ip = pack(ip, cidr); ++ DP("%u.%u.%u.%u/%u, %u.%u.%u.%u", HIPQUAD(ip), cidr, HIPQUAD(*hash_ip)); ++ ++ return __addip_base(map, *hash_ip); ++} ++ ++static void ++update_cidr_sizes(struct ip_set_nethash *map, unsigned char cidr) ++{ ++ unsigned char next; ++ int i; ++ ++ for (i = 0; i < 30 && map->cidr[i]; i++) { ++ if (map->cidr[i] == cidr) { ++ return; ++ } else if (map->cidr[i] < cidr) { ++ next = map->cidr[i]; ++ map->cidr[i] = cidr; ++ cidr = next; ++ } ++ } ++ if (i < 30) ++ map->cidr[i] = cidr; ++} ++ ++static int ++addip(struct ip_set *set, const void *data, size_t size, ++ ip_set_ip_t *hash_ip) ++{ ++ struct ip_set_req_nethash *req = ++ (struct ip_set_req_nethash *) data; ++ int ret; ++ ++ if (size != sizeof(struct ip_set_req_nethash)) { ++ ip_set_printk("data length wrong (want %zu, have %zu)", ++ sizeof(struct ip_set_req_nethash), ++ size); ++ return -EINVAL; ++ } ++ ret = __addip((struct ip_set_nethash *) set->data, ++ req->ip, req->cidr, hash_ip); ++ ++ if (ret == 0) ++ update_cidr_sizes((struct ip_set_nethash *) set->data, ++ req->cidr); ++ ++ return ret; ++} ++ ++static int ++addip_kernel(struct ip_set *set, const struct sk_buff *skb, ++ u_int32_t flags, ip_set_ip_t *hash_ip) ++{ ++ struct ip_set_nethash *map = (struct ip_set_nethash *) set->data; ++ int ret = -ERANGE; ++ ip_set_ip_t ip = ntohl(flags & IPSET_SRC ? skb->nh.iph->saddr ++ : skb->nh.iph->daddr); ++ ++ if (map->cidr[0]) ++ ret = __addip(map, ip, map->cidr[0], hash_ip); ++ ++ return ret; ++} ++ ++static int retry(struct ip_set *set) ++{ ++ struct ip_set_nethash *map = (struct ip_set_nethash *) set->data; ++ ip_set_ip_t *members; ++ u_int32_t i, hashsize; ++ unsigned newbytes; ++ int res; ++ struct ip_set_nethash tmp = { ++ .hashsize = map->hashsize, ++ .probes = map->probes, ++ .resize = map->resize ++ }; ++ ++ if (map->resize == 0) ++ return -ERANGE; ++ ++ memcpy(tmp.cidr, map->cidr, 30 * sizeof(unsigned char)); ++ again: ++ res = 0; ++ ++ /* Calculate new parameters */ ++ get_random_bytes(&tmp.initval, 4); ++ hashsize = tmp.hashsize + (tmp.hashsize * map->resize)/100; ++ if (hashsize == tmp.hashsize) ++ hashsize++; ++ tmp.prime = make_prime(hashsize); ++ ++ ip_set_printk("rehashing of set %s triggered: " ++ "hashsize grows from %u to %u", ++ set->name, tmp.hashsize, hashsize); ++ tmp.hashsize = hashsize; ++ ++ newbytes = hashsize * sizeof(ip_set_ip_t); ++ tmp.members = ip_set_malloc(newbytes); ++ if (!tmp.members) { ++ DP("out of memory for %d bytes", newbytes); ++ return -ENOMEM; ++ } ++ memset(tmp.members, 0, newbytes); ++ ++ write_lock_bh(&set->lock); ++ map = (struct ip_set_nethash *) set->data; /* Play safe */ ++ for (i = 0; i < map->hashsize && res == 0; i++) { ++ if (map->members[i]) ++ res = __addip_base(&tmp, map->members[i]); ++ } ++ if (res) { ++ /* Failure, try again */ ++ write_unlock_bh(&set->lock); ++ ip_set_free(tmp.members, newbytes); ++ goto again; ++ } ++ ++ /* Success at resizing! */ ++ members = map->members; ++ hashsize = map->hashsize; ++ ++ map->initval = tmp.initval; ++ map->prime = tmp.prime; ++ map->hashsize = tmp.hashsize; ++ map->members = tmp.members; ++ write_unlock_bh(&set->lock); ++ ++ ip_set_free(members, hashsize * sizeof(ip_set_ip_t)); ++ ++ return 0; ++} ++ ++static inline int ++__delip(struct ip_set_nethash *map, ip_set_ip_t ip, unsigned char cidr, ++ ip_set_ip_t *hash_ip) ++{ ++ ip_set_ip_t id = hash_id_cidr(map, ip, cidr, hash_ip); ++ ++ if (id == UINT_MAX) ++ return -EEXIST; ++ ++ map->members[id] = 0; ++ return 0; ++} ++ ++static int ++delip(struct ip_set *set, const void *data, size_t size, ++ ip_set_ip_t *hash_ip) ++{ ++ struct ip_set_req_nethash *req = ++ (struct ip_set_req_nethash *) data; ++ ++ if (size != sizeof(struct ip_set_req_nethash)) { ++ ip_set_printk("data length wrong (want %zu, have %zu)", ++ sizeof(struct ip_set_req_nethash), ++ size); ++ return -EINVAL; ++ } ++ /* TODO: no garbage collection in map->cidr */ ++ return __delip((struct ip_set_nethash *) set->data, ++ req->ip, req->cidr, hash_ip); ++} ++ ++static int ++delip_kernel(struct ip_set *set, const struct sk_buff *skb, ++ u_int32_t flags, ip_set_ip_t *hash_ip) ++{ ++ struct ip_set_nethash *map = (struct ip_set_nethash *) set->data; ++ int ret = -ERANGE; ++ ip_set_ip_t ip = ntohl(flags & IPSET_SRC ? skb->nh.iph->saddr ++ : skb->nh.iph->daddr); ++ ++ if (map->cidr[0]) ++ ret = __delip(map, ip, map->cidr[0], hash_ip); ++ ++ return ret; ++} ++ ++static int create(struct ip_set *set, const void *data, size_t size) ++{ ++ unsigned newbytes; ++ struct ip_set_req_nethash_create *req = ++ (struct ip_set_req_nethash_create *) data; ++ struct ip_set_nethash *map; ++ ++ if (size != sizeof(struct ip_set_req_nethash_create)) { ++ ip_set_printk("data length wrong (want %zu, have %zu)", ++ sizeof(struct ip_set_req_nethash_create), ++ size); ++ return -EINVAL; ++ } ++ ++ if (req->hashsize < 1) { ++ ip_set_printk("hashsize too small"); ++ return -ENOEXEC; ++ } ++ ++ map = kmalloc(sizeof(struct ip_set_nethash), GFP_KERNEL); ++ if (!map) { ++ DP("out of memory for %d bytes", ++ sizeof(struct ip_set_nethash)); ++ return -ENOMEM; ++ } ++ get_random_bytes(&map->initval, 4); ++ map->prime = make_prime(req->hashsize); ++ map->hashsize = req->hashsize; ++ map->probes = req->probes; ++ map->resize = req->resize; ++ memset(map->cidr, 0, 30 * sizeof(unsigned char)); ++ newbytes = map->hashsize * sizeof(ip_set_ip_t); ++ map->members = ip_set_malloc(newbytes); ++ if (!map->members) { ++ DP("out of memory for %d bytes", newbytes); ++ kfree(map); ++ return -ENOMEM; ++ } ++ memset(map->members, 0, newbytes); ++ ++ set->data = map; ++ return 0; ++} ++ ++static void destroy(struct ip_set *set) ++{ ++ struct ip_set_nethash *map = (struct ip_set_nethash *) set->data; ++ ++ ip_set_free(map->members, map->hashsize * sizeof(ip_set_ip_t)); ++ kfree(map); ++ ++ set->data = NULL; ++} ++ ++static void flush(struct ip_set *set) ++{ ++ struct ip_set_nethash *map = (struct ip_set_nethash *) set->data; ++ memset(map->members, 0, map->hashsize * sizeof(ip_set_ip_t)); ++ memset(map->cidr, 0, 30 * sizeof(unsigned char)); ++} ++ ++static void list_header(const struct ip_set *set, void *data) ++{ ++ struct ip_set_nethash *map = (struct ip_set_nethash *) set->data; ++ struct ip_set_req_nethash_create *header = ++ (struct ip_set_req_nethash_create *) data; ++ ++ header->hashsize = map->hashsize; ++ header->probes = map->probes; ++ header->resize = map->resize; ++} ++ ++static int list_members_size(const struct ip_set *set) ++{ ++ struct ip_set_nethash *map = (struct ip_set_nethash *) set->data; ++ ++ return (map->hashsize * sizeof(ip_set_ip_t)); ++} ++ ++static void list_members(const struct ip_set *set, void *data) ++{ ++ struct ip_set_nethash *map = (struct ip_set_nethash *) set->data; ++ int bytes = map->hashsize * sizeof(ip_set_ip_t); ++ ++ memcpy(data, map->members, bytes); ++} ++ ++static struct ip_set_type ip_set_nethash = { ++ .typename = SETTYPE_NAME, ++ .typecode = IPSET_TYPE_IP, ++ .protocol_version = IP_SET_PROTOCOL_VERSION, ++ .create = &create, ++ .destroy = &destroy, ++ .flush = &flush, ++ .reqsize = sizeof(struct ip_set_req_nethash), ++ .addip = &addip, ++ .addip_kernel = &addip_kernel, ++ .retry = &retry, ++ .delip = &delip, ++ .delip_kernel = &delip_kernel, ++ .testip = &testip, ++ .testip_kernel = &testip_kernel, ++ .header_size = sizeof(struct ip_set_req_nethash_create), ++ .list_header = &list_header, ++ .list_members_size = &list_members_size, ++ .list_members = &list_members, ++ .me = THIS_MODULE, ++}; ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>"); ++MODULE_DESCRIPTION("nethash type of IP sets"); ++ ++static int __init init(void) ++{ ++ return ip_set_register_set_type(&ip_set_nethash); ++} ++ ++static void __exit fini(void) ++{ ++ /* FIXME: possible race with ip_set_create() */ ++ ip_set_unregister_set_type(&ip_set_nethash); ++} ++ ++module_init(init); ++module_exit(fini); +diff -Naur linux-old/net/ipv4/netfilter/ip_set_portmap.c linux-new/net/ipv4/netfilter/ip_set_portmap.c +--- linux-old/net/ipv4/netfilter/ip_set_portmap.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-new/net/ipv4/netfilter/ip_set_portmap.c 2005-08-10 22:13:00.000000000 +0200 +@@ -0,0 +1,321 @@ ++/* Copyright (C) 2003-2004 Jozsef Kadlecsik <kadlec@blackhole.kfki.hu> ++ * ++ * 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. ++ */ ++ ++/* Kernel module implementing a port set type as a bitmap */ ++ ++#include <linux/module.h> ++#include <linux/ip.h> ++#include <linux/tcp.h> ++#include <linux/udp.h> ++#include <linux/skbuff.h> ++#include <linux/netfilter_ipv4/ip_tables.h> ++#include <linux/netfilter_ipv4/ip_set.h> ++#include <linux/errno.h> ++#include <asm/uaccess.h> ++#include <asm/bitops.h> ++#include <asm/softirq.h> ++#include <linux/spinlock.h> ++ ++#include <net/ip.h> ++ ++#include <linux/netfilter_ipv4/ip_set_portmap.h> ++ ++static inline ip_set_ip_t ++get_port(const struct sk_buff *skb, u_int32_t flags) ++{ ++ struct iphdr *iph = skb->nh.iph; ++ u_int16_t offset = ntohs(iph->frag_off) & IP_OFFSET; ++ ++ switch (iph->protocol) { ++ case IPPROTO_TCP: { ++ struct tcphdr *tcph = (struct tcphdr *)((u_int32_t *)iph + iph->ihl); ++ ++ /* See comments at tcp_match in ip_tables.c */ ++ if (offset != 0 ++ || (offset == 0 ++ && (skb->len - iph->ihl * 4) < sizeof(struct tcphdr))) ++ return INVALID_PORT; ++ ++ return ntohs(flags & IPSET_SRC ? ++ tcph->source : tcph->dest); ++ } ++ case IPPROTO_UDP: { ++ struct udphdr *udph = (struct udphdr *)((u_int32_t *)iph + iph->ihl); ++ ++ if (offset != 0 ++ || (offset == 0 ++ && (skb->len - iph->ihl * 4) < sizeof(struct udphdr))) ++ return INVALID_PORT; ++ ++ return ntohs(flags & IPSET_SRC ? ++ udph->source : udph->dest); ++ } ++ default: ++ return INVALID_PORT; ++ } ++} ++ ++static inline int ++__testport(struct ip_set *set, ip_set_ip_t port, ip_set_ip_t *hash_port) ++{ ++ struct ip_set_portmap *map = (struct ip_set_portmap *) set->data; ++ ++ if (port < map->first_port || port > map->last_port) ++ return -ERANGE; ++ ++ *hash_port = port; ++ DP("set: %s, port:%u, %u", set->name, port, *hash_port); ++ return !!test_bit(port - map->first_port, map->members); ++} ++ ++static int ++testport(struct ip_set *set, const void *data, size_t size, ++ ip_set_ip_t *hash_port) ++{ ++ struct ip_set_req_portmap *req = ++ (struct ip_set_req_portmap *) data; ++ ++ if (size != sizeof(struct ip_set_req_portmap)) { ++ ip_set_printk("data length wrong (want %zu, have %zu)", ++ sizeof(struct ip_set_req_portmap), ++ size); ++ return -EINVAL; ++ } ++ return __testport(set, req->port, hash_port); ++} ++ ++static int ++testport_kernel(struct ip_set *set, const struct sk_buff *skb, ++ u_int32_t flags, ip_set_ip_t *hash_port) ++{ ++ int res; ++ ip_set_ip_t port = get_port(skb, flags); ++ ++ DP("flag %s port %u", flags & IPSET_SRC ? "SRC" : "DST", port); ++ if (port == INVALID_PORT) ++ return 0; ++ ++ res = __testport(set, port, hash_port); ++ ++ return (res < 0 ? 0 : res); ++} ++ ++static inline int ++__addport(struct ip_set *set, ip_set_ip_t port, ip_set_ip_t *hash_port) ++{ ++ struct ip_set_portmap *map = (struct ip_set_portmap *) set->data; ++ ++ if (port < map->first_port || port > map->last_port) ++ return -ERANGE; ++ if (test_and_set_bit(port - map->first_port, map->members)) ++ return -EEXIST; ++ ++ *hash_port = port; ++ DP("port %u", port); ++ return 0; ++} ++ ++static int ++addport(struct ip_set *set, const void *data, size_t size, ++ ip_set_ip_t *hash_port) ++{ ++ struct ip_set_req_portmap *req = ++ (struct ip_set_req_portmap *) data; ++ ++ if (size != sizeof(struct ip_set_req_portmap)) { ++ ip_set_printk("data length wrong (want %zu, have %zu)", ++ sizeof(struct ip_set_req_portmap), ++ size); ++ return -EINVAL; ++ } ++ return __addport(set, req->port, hash_port); ++} ++ ++static int ++addport_kernel(struct ip_set *set, const struct sk_buff *skb, ++ u_int32_t flags, ip_set_ip_t *hash_port) ++{ ++ ip_set_ip_t port = get_port(skb, flags); ++ ++ if (port == INVALID_PORT) ++ return -EINVAL; ++ ++ return __addport(set, port, hash_port); ++} ++ ++static inline int ++__delport(struct ip_set *set, ip_set_ip_t port, ip_set_ip_t *hash_port) ++{ ++ struct ip_set_portmap *map = (struct ip_set_portmap *) set->data; ++ ++ if (port < map->first_port || port > map->last_port) ++ return -ERANGE; ++ if (!test_and_clear_bit(port - map->first_port, map->members)) ++ return -EEXIST; ++ ++ *hash_port = port; ++ DP("port %u", port); ++ return 0; ++} ++ ++static int ++delport(struct ip_set *set, const void *data, size_t size, ++ ip_set_ip_t *hash_port) ++{ ++ struct ip_set_req_portmap *req = ++ (struct ip_set_req_portmap *) data; ++ ++ if (size != sizeof(struct ip_set_req_portmap)) { ++ ip_set_printk("data length wrong (want %zu, have %zu)", ++ sizeof(struct ip_set_req_portmap), ++ size); ++ return -EINVAL; ++ } ++ return __delport(set, req->port, hash_port); ++} ++ ++static int ++delport_kernel(struct ip_set *set, const struct sk_buff *skb, ++ u_int32_t flags, ip_set_ip_t *hash_port) ++{ ++ ip_set_ip_t port = get_port(skb, flags); ++ ++ if (port == INVALID_PORT) ++ return -EINVAL; ++ ++ return __delport(set, port, hash_port); ++} ++ ++static int create(struct ip_set *set, const void *data, size_t size) ++{ ++ int newbytes; ++ struct ip_set_req_portmap_create *req = ++ (struct ip_set_req_portmap_create *) data; ++ struct ip_set_portmap *map; ++ ++ if (size != sizeof(struct ip_set_req_portmap_create)) { ++ ip_set_printk("data length wrong (want %zu, have %zu)", ++ sizeof(struct ip_set_req_portmap_create), ++ size); ++ return -EINVAL; ++ } ++ ++ DP("from %u to %u", req->from, req->to); ++ ++ if (req->from > req->to) { ++ DP("bad port range"); ++ return -ENOEXEC; ++ } ++ ++ if (req->to - req->from > MAX_RANGE) { ++ ip_set_printk("range too big (max %d ports)", ++ MAX_RANGE); ++ return -ENOEXEC; ++ } ++ ++ map = kmalloc(sizeof(struct ip_set_portmap), GFP_KERNEL); ++ if (!map) { ++ DP("out of memory for %d bytes", ++ sizeof(struct ip_set_portmap)); ++ return -ENOMEM; ++ } ++ map->first_port = req->from; ++ map->last_port = req->to; ++ newbytes = bitmap_bytes(req->from, req->to); ++ map->members = kmalloc(newbytes, GFP_KERNEL); ++ if (!map->members) { ++ DP("out of memory for %d bytes", newbytes); ++ kfree(map); ++ return -ENOMEM; ++ } ++ memset(map->members, 0, newbytes); ++ ++ set->data = map; ++ return 0; ++} ++ ++static void destroy(struct ip_set *set) ++{ ++ struct ip_set_portmap *map = (struct ip_set_portmap *) set->data; ++ ++ kfree(map->members); ++ kfree(map); ++ ++ set->data = NULL; ++} ++ ++static void flush(struct ip_set *set) ++{ ++ struct ip_set_portmap *map = (struct ip_set_portmap *) set->data; ++ memset(map->members, 0, bitmap_bytes(map->first_port, map->last_port)); ++} ++ ++static void list_header(const struct ip_set *set, void *data) ++{ ++ struct ip_set_portmap *map = (struct ip_set_portmap *) set->data; ++ struct ip_set_req_portmap_create *header = ++ (struct ip_set_req_portmap_create *) data; ++ ++ DP("list_header %u %u", map->first_port, map->last_port); ++ ++ header->from = map->first_port; ++ header->to = map->last_port; ++} ++ ++static int list_members_size(const struct ip_set *set) ++{ ++ struct ip_set_portmap *map = (struct ip_set_portmap *) set->data; ++ ++ return bitmap_bytes(map->first_port, map->last_port); ++} ++ ++static void list_members(const struct ip_set *set, void *data) ++{ ++ struct ip_set_portmap *map = (struct ip_set_portmap *) set->data; ++ int bytes = bitmap_bytes(map->first_port, map->last_port); ++ ++ memcpy(data, map->members, bytes); ++} ++ ++static struct ip_set_type ip_set_portmap = { ++ .typename = SETTYPE_NAME, ++ .typecode = IPSET_TYPE_PORT, ++ .protocol_version = IP_SET_PROTOCOL_VERSION, ++ .create = &create, ++ .destroy = &destroy, ++ .flush = &flush, ++ .reqsize = sizeof(struct ip_set_req_portmap), ++ .addip = &addport, ++ .addip_kernel = &addport_kernel, ++ .delip = &delport, ++ .delip_kernel = &delport_kernel, ++ .testip = &testport, ++ .testip_kernel = &testport_kernel, ++ .header_size = sizeof(struct ip_set_req_portmap_create), ++ .list_header = &list_header, ++ .list_members_size = &list_members_size, ++ .list_members = &list_members, ++ .me = THIS_MODULE, ++}; ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>"); ++MODULE_DESCRIPTION("portmap type of IP sets"); ++ ++static int __init init(void) ++{ ++ return ip_set_register_set_type(&ip_set_portmap); ++} ++ ++static void __exit fini(void) ++{ ++ /* FIXME: possible race with ip_set_create() */ ++ ip_set_unregister_set_type(&ip_set_portmap); ++} ++ ++module_init(init); ++module_exit(fini); +diff -Naur linux-old/net/ipv4/netfilter/ipt_set.c linux-new/net/ipv4/netfilter/ipt_set.c +--- linux-old/net/ipv4/netfilter/ipt_set.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-new/net/ipv4/netfilter/ipt_set.c 2005-08-10 22:13:00.000000000 +0200 +@@ -0,0 +1,114 @@ ++/* Copyright (C) 2000-2002 Joakim Axelsson <gozem@linux.nu> ++ * Patrick Schaaf <bof@bof.de> ++ * Martin Josefsson <gandalf@wlug.westbo.se> ++ * Copyright (C) 2003-2004 Jozsef Kadlecsik <kadlec@blackhole.kfki.hu> ++ * ++ * 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. ++ */ ++ ++/* Kernel module to match an IP set. */ ++ ++#include <linux/module.h> ++#include <linux/ip.h> ++#include <linux/skbuff.h> ++ ++#include <linux/netfilter_ipv4/ip_tables.h> ++#include <linux/netfilter_ipv4/ip_set.h> ++#include <linux/netfilter_ipv4/ipt_set.h> ++ ++static inline int ++match_set(const struct ipt_set_info *info, ++ const struct sk_buff *skb, ++ int inv) ++{ ++ if (ip_set_testip_kernel(info->index, skb, info->flags)) ++ inv = !inv; ++ return inv; ++} ++ ++static int ++match(const struct sk_buff *skb, ++ const struct net_device *in, ++ const struct net_device *out, ++ const void *matchinfo, ++ int offset, ++ const void *hdr, ++ u_int16_t datalen, ++ int *hotdrop) ++{ ++ const struct ipt_set_info_match *info = matchinfo; ++ ++ return match_set(&info->match_set, ++ skb, ++ info->match_set.flags[0] & IPSET_MATCH_INV); ++} ++ ++static int ++checkentry(const char *tablename, ++ const struct ipt_ip *ip, ++ void *matchinfo, ++ unsigned int matchsize, ++ unsigned int hook_mask) ++{ ++ struct ipt_set_info_match *info = ++ (struct ipt_set_info_match *) matchinfo; ++ ip_set_id_t index; ++ ++ if (matchsize != IPT_ALIGN(sizeof(struct ipt_set_info_match))) { ++ ip_set_printk("invalid matchsize %d", matchsize); ++ return 0; ++ } ++ ++ index = ip_set_get_byindex(info->match_set.index); ++ ++ if (index == IP_SET_INVALID_ID) { ++ ip_set_printk("Cannot find set indentified by id %u to match", ++ info->match_set.index); ++ return 0; /* error */ ++ } ++ if (info->match_set.flags[IP_SET_MAX_BINDINGS] != 0) { ++ ip_set_printk("That's nasty!"); ++ return 0; /* error */ ++ } ++ ++ return 1; ++} ++ ++static void destroy(void *matchinfo, unsigned int matchsize) ++{ ++ struct ipt_set_info_match *info = matchinfo; ++ ++ if (matchsize != IPT_ALIGN(sizeof(struct ipt_set_info_match))) { ++ ip_set_printk("invalid matchsize %d", matchsize); ++ return; ++ } ++ ++ ip_set_put(info->match_set.index); ++} ++ ++static struct ipt_match set_match = { ++ .name = "set", ++ .match = &match, ++ .checkentry = &checkentry, ++ .destroy = &destroy, ++ .me = THIS_MODULE ++}; ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>"); ++MODULE_DESCRIPTION("iptables IP set match module"); ++ ++static int __init init(void) ++{ ++ return ipt_register_match(&set_match); ++} ++ ++static void __exit fini(void) ++{ ++ ipt_unregister_match(&set_match); ++} ++ ++module_init(init); ++module_exit(fini); +diff -Naur linux-old/net/ipv4/netfilter/ipt_SET.c linux-new/net/ipv4/netfilter/ipt_SET.c +--- linux-old/net/ipv4/netfilter/ipt_SET.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-new/net/ipv4/netfilter/ipt_SET.c 2005-08-10 22:13:00.000000000 +0200 +@@ -0,0 +1,127 @@ ++/* Copyright (C) 2000-2002 Joakim Axelsson <gozem@linux.nu> ++ * Patrick Schaaf <bof@bof.de> ++ * Martin Josefsson <gandalf@wlug.westbo.se> ++ * Copyright (C) 2003-2004 Jozsef Kadlecsik <kadlec@blackhole.kfki.hu> ++ * ++ * 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. ++ */ ++ ++/* ipt_SET.c - netfilter target to manipulate IP sets */ ++ ++#include <linux/types.h> ++#include <linux/ip.h> ++#include <linux/timer.h> ++#include <linux/module.h> ++#include <linux/netfilter.h> ++#include <linux/netdevice.h> ++#include <linux/if.h> ++#include <linux/inetdevice.h> ++#include <net/protocol.h> ++#include <net/checksum.h> ++#include <linux/netfilter_ipv4.h> ++#include <linux/netfilter_ipv4/ip_nat_rule.h> ++#include <linux/netfilter_ipv4/ipt_set.h> ++ ++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) ++{ ++ const struct ipt_set_info_target *info = targinfo; ++ ++ if (info->add_set.index != IP_SET_INVALID_ID) ++ ip_set_addip_kernel(info->add_set.index, ++ *pskb, ++ info->add_set.flags); ++ if (info->del_set.index != IP_SET_INVALID_ID) ++ ip_set_delip_kernel(info->del_set.index, ++ *pskb, ++ info->del_set.flags); ++ ++ 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_set_info_target *info = ++ (struct ipt_set_info_target *) targinfo; ++ ip_set_id_t index; ++ ++ if (targinfosize != IPT_ALIGN(sizeof(*info))) { ++ DP("bad target info size %u", targinfosize); ++ return 0; ++ } ++ ++ if (info->add_set.index != IP_SET_INVALID_ID) { ++ index = ip_set_get_byindex(info->add_set.index); ++ if (index == IP_SET_INVALID_ID) { ++ ip_set_printk("cannot find add_set index %u as target", ++ info->add_set.index); ++ return 0; /* error */ ++ } ++ } ++ ++ if (info->del_set.index != IP_SET_INVALID_ID) { ++ index = ip_set_get_byindex(info->del_set.index); ++ if (index == IP_SET_INVALID_ID) { ++ ip_set_printk("cannot find del_set index %u as target", ++ info->del_set.index); ++ return 0; /* error */ ++ } ++ } ++ if (info->add_set.flags[IP_SET_MAX_BINDINGS] != 0 ++ || info->del_set.flags[IP_SET_MAX_BINDINGS] != 0) { ++ ip_set_printk("That's nasty!"); ++ return 0; /* error */ ++ } ++ return 1; ++} ++ ++static void destroy(void *targetinfo, unsigned int targetsize) ++{ ++ struct ipt_set_info_target *info = targetinfo; ++ ++ if (targetsize != IPT_ALIGN(sizeof(struct ipt_set_info_target))) { ++ ip_set_printk("invalid targetsize %d", targetsize); ++ return; ++ } ++ ++ if (info->add_set.index != IP_SET_INVALID_ID) ++ ip_set_put(info->add_set.index); ++ if (info->del_set.index != IP_SET_INVALID_ID) ++ ip_set_put(info->del_set.index); ++} ++ ++static struct ipt_target SET_target = { ++ .name = "SET", ++ .target = target, ++ .checkentry = checkentry, ++ .destroy = destroy, ++ .me = THIS_MODULE ++}; ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>"); ++MODULE_DESCRIPTION("iptables IP set target module"); ++ ++static int __init init(void) ++{ ++ return ipt_register_target(&SET_target); ++} ++ ++static void __exit fini(void) ++{ ++ ipt_unregister_target(&SET_target); ++} ++ ++module_init(init); ++module_exit(fini); +diff -Naur linux-old/net/ipv4/netfilter/Makefile linux-new/net/ipv4/netfilter/Makefile +--- linux-old/net/ipv4/netfilter/Makefile 2005-07-23 23:34:46.000000000 +0200 ++++ linux-new/net/ipv4/netfilter/Makefile 2005-08-10 22:13:01.000000000 +0200 +@@ -75,6 +75,18 @@ + obj-$(CONFIG_IP_NF_MATCH_HELPER) += ipt_helper.o + obj-$(CONFIG_IP_NF_MATCH_LIMIT) += ipt_limit.o + obj-$(CONFIG_IP_NF_MATCH_MARK) += ipt_mark.o ++obj-$(CONFIG_IP_NF_MATCH_SET) += ipt_set.o ++obj-$(CONFIG_IP_NF_TARGET_SET) += ipt_SET.o ++ifdef CONFIG_IP_NF_SET ++ obj-$(CONFIG_IP_NF_SET) += ip_set.o ++ export-objs += ip_set.o ++endif ++obj-$(CONFIG_IP_NF_SET_IPMAP) += ip_set_ipmap.o ++obj-$(CONFIG_IP_NF_SET_PORTMAP) += ip_set_portmap.o ++obj-$(CONFIG_IP_NF_SET_MACIPMAP) += ip_set_macipmap.o ++obj-$(CONFIG_IP_NF_SET_IPHASH) += ip_set_iphash.o ++obj-$(CONFIG_IP_NF_SET_NETHASH) += ip_set_nethash.o ++obj-$(CONFIG_IP_NF_SET_IPTREE) += ip_set_iptree.o + obj-$(CONFIG_IP_NF_MATCH_MAC) += ipt_mac.o + obj-$(CONFIG_IP_NF_MATCH_IPP2P) += ipt_ipp2p.o + |