]>
Commit | Line | Data |
---|---|---|
44d6f47a JE |
1 | #include <linux/module.h> |
2 | #include <linux/version.h> | |
3 | #include <linux/netfilter_ipv4/ip_tables.h> | |
4 | #include <net/tcp.h> | |
5 | #include <net/udp.h> | |
0712d0fd | 6 | #include <asm/unaligned.h> |
44d6f47a JE |
7 | #include "xt_ipp2p.h" |
8 | #include "compat_xtables.h" | |
9 | ||
c237fe24 JE |
10 | //#define IPP2P_DEBUG_ARES |
11 | //#define IPP2P_DEBUG_SOUL | |
12 | //#define IPP2P_DEBUG_WINMX | |
13 | ||
74e7eb28 JE |
14 | #define get_u8(X, O) (*(const __u8 *)((X) + O)) |
15 | #define get_u16(X, O) get_unaligned((const __u16 *)((X) + O)) | |
16 | #define get_u32(X, O) get_unaligned((const __u32 *)((X) + O)) | |
44d6f47a JE |
17 | |
18 | MODULE_AUTHOR("Eicke Friedrich/Klaus Degner <ipp2p@ipp2p.org>"); | |
19 | MODULE_DESCRIPTION("An extension to iptables to identify P2P traffic."); | |
20 | MODULE_LICENSE("GPL"); | |
21 | ||
c237fe24 | 22 | /* Search for UDP eDonkey/eMule/Kad commands */ |
569643ac | 23 | static unsigned int |
cb407ce7 | 24 | udp_search_edk(const unsigned char *t, const unsigned int packet_len) |
44d6f47a | 25 | { |
17a03128 CB |
26 | if (packet_len < 4) |
27 | return 0; | |
28 | ||
44d6f47a | 29 | switch (t[0]) { |
c237fe24 JE |
30 | case 0xe3: |
31 | /* edonkey */ | |
32 | switch (t[1]) { | |
33 | /* client -> server status request */ | |
34 | case 0x96: | |
c66d291e | 35 | if (packet_len == 6) |
c237fe24 | 36 | return IPP2P_EDK * 100 + 50; |
44d6f47a | 37 | break; |
c237fe24 JE |
38 | |
39 | /* server -> client status request */ | |
40 | case 0x97: | |
c66d291e | 41 | if (packet_len == 34) |
c237fe24 JE |
42 | return IPP2P_EDK * 100 + 51; |
43 | break; | |
44 | ||
45 | /* server description request */ | |
46 | /* e3 2a ff f0 .. | size == 6 */ | |
47 | case 0xa2: | |
c66d291e | 48 | if (packet_len == 6 && |
c237fe24 JE |
49 | get_u16(t, 2) == __constant_htons(0xfff0)) |
50 | return IPP2P_EDK * 100 + 52; | |
51 | break; | |
52 | ||
53 | /* server description response */ | |
54 | /* e3 a3 ff f0 .. | size > 40 && size < 200 */ | |
55 | /* | |
56 | case 0xa3: | |
57 | return IPP2P_EDK * 100 + 53; | |
58 | break; | |
59 | */ | |
60 | ||
61 | case 0x9a: | |
c66d291e | 62 | if (packet_len == 18) |
c237fe24 JE |
63 | return IPP2P_EDK * 100 + 54; |
64 | break; | |
65 | ||
66 | case 0x92: | |
c66d291e | 67 | if (packet_len == 10) |
c237fe24 | 68 | return IPP2P_EDK * 100 + 55; |
44d6f47a JE |
69 | break; |
70 | } | |
c237fe24 JE |
71 | break; |
72 | ||
73 | case 0xe4: | |
74 | switch (t[1]) { | |
c66d291e | 75 | /* e4 20 .. | size == 35 */ |
c237fe24 | 76 | case 0x20: |
c66d291e | 77 | if (packet_len == 35 && t[2] != 0x00 && t[34] != 0x00) |
c237fe24 JE |
78 | return IPP2P_EDK * 100 + 60; |
79 | break; | |
44d6f47a | 80 | |
c66d291e | 81 | /* e4 00 .. 00 | size == 27 ? */ |
c237fe24 | 82 | case 0x00: |
c66d291e | 83 | if (packet_len == 27 && t[26] == 0x00) |
c237fe24 | 84 | return IPP2P_EDK * 100 + 61; |
44d6f47a | 85 | break; |
c237fe24 | 86 | |
c66d291e | 87 | /* e4 10 .. 00 | size == 27 ? */ |
c237fe24 | 88 | case 0x10: |
c66d291e | 89 | if (packet_len == 27 && t[26] == 0x00) |
c237fe24 | 90 | return IPP2P_EDK * 100 + 62; |
44d6f47a | 91 | break; |
c237fe24 | 92 | |
c66d291e | 93 | /* e4 18 .. 00 | size == 27 ? */ |
c237fe24 | 94 | case 0x18: |
c66d291e | 95 | if (packet_len == 27 && t[26] == 0x00) |
c237fe24 | 96 | return IPP2P_EDK * 100 + 63; |
44d6f47a | 97 | break; |
c237fe24 | 98 | |
c66d291e | 99 | /* e4 52 .. | size = 36 */ |
c237fe24 | 100 | case 0x52: |
c66d291e | 101 | if (packet_len == 36) |
c237fe24 | 102 | return IPP2P_EDK * 100 + 64; |
44d6f47a | 103 | break; |
c237fe24 JE |
104 | |
105 | /* e4 58 .. | size == 6 */ | |
106 | case 0x58: | |
c66d291e | 107 | if (packet_len == 6) |
c237fe24 | 108 | return IPP2P_EDK * 100 + 65; |
44d6f47a | 109 | break; |
c237fe24 JE |
110 | |
111 | /* e4 59 .. | size == 2 */ | |
112 | case 0x59: | |
c66d291e | 113 | if (packet_len == 2) |
c237fe24 | 114 | return IPP2P_EDK * 100 + 66; |
44d6f47a | 115 | break; |
c237fe24 | 116 | |
c66d291e | 117 | /* e4 28 .. | packet_len == 49,69,94,119... */ |
c237fe24 | 118 | case 0x28: |
c66d291e | 119 | if ((packet_len - 44) % 25 == 0) |
c237fe24 | 120 | return IPP2P_EDK * 100 + 67; |
44d6f47a | 121 | break; |
c237fe24 JE |
122 | |
123 | /* e4 50 xx xx | size == 4 */ | |
124 | case 0x50: | |
c66d291e | 125 | if (packet_len == 4) |
c237fe24 JE |
126 | return IPP2P_EDK * 100 + 68; |
127 | break; | |
128 | ||
129 | /* e4 40 xx xx | size == 48 */ | |
130 | case 0x40: | |
c66d291e | 131 | if (packet_len == 48) |
c237fe24 | 132 | return IPP2P_EDK * 100 + 69; |
44d6f47a | 133 | break; |
44d6f47a | 134 | } |
c237fe24 | 135 | break; |
44d6f47a | 136 | } |
c237fe24 JE |
137 | return 0; |
138 | } | |
139 | ||
140 | /* Search for UDP Gnutella commands */ | |
569643ac | 141 | static unsigned int |
cb407ce7 | 142 | udp_search_gnu(const unsigned char *t, const unsigned int packet_len) |
c237fe24 | 143 | { |
17a03128 | 144 | if (packet_len >= 3 && memcmp(t, "GND", 3) == 0) |
c237fe24 | 145 | return IPP2P_GNU * 100 + 51; |
17a03128 | 146 | if (packet_len >= 9 && memcmp(t, "GNUTELLA ", 9) == 0) |
c237fe24 JE |
147 | return IPP2P_GNU * 100 + 52; |
148 | return 0; | |
149 | } | |
150 | ||
151 | /* Search for UDP KaZaA commands */ | |
569643ac | 152 | static unsigned int |
cb407ce7 | 153 | udp_search_kazaa(const unsigned char *t, const unsigned int packet_len) |
c237fe24 | 154 | { |
17a03128 CB |
155 | if (packet_len < 6) |
156 | return 0; | |
157 | if (memcmp(t + packet_len - 6, "KaZaA\x00", 6) == 0) | |
158 | return IPP2P_KAZAA * 100 + 50; | |
c237fe24 JE |
159 | return 0; |
160 | } | |
161 | ||
162 | /* Search for UDP DirectConnect commands */ | |
cb407ce7 | 163 | static unsigned int udp_search_directconnect(const unsigned char *t, |
569643ac | 164 | const unsigned int packet_len) |
c237fe24 | 165 | { |
17a03128 CB |
166 | if (packet_len < 5) |
167 | return 0; | |
168 | if (t[0] == 0x24 && t[packet_len-1] == 0x7c) { | |
169 | if (memcmp(&t[1], "SR ", 3) == 0) | |
c237fe24 | 170 | return IPP2P_DC * 100 + 60; |
17a03128 | 171 | if (packet_len >= 7 && memcmp(&t[1], "Ping ", 5) == 0) |
c237fe24 JE |
172 | return IPP2P_DC * 100 + 61; |
173 | } | |
174 | return 0; | |
175 | } | |
176 | ||
177 | /* Search for UDP BitTorrent commands */ | |
569643ac JE |
178 | static unsigned int |
179 | udp_search_bit(const unsigned char *haystack, const unsigned int packet_len) | |
c237fe24 JE |
180 | { |
181 | switch (packet_len) { | |
c66d291e | 182 | case 16: |
c237fe24 | 183 | /* ^ 00 00 04 17 27 10 19 80 */ |
c66d291e CB |
184 | if (ntohl(get_u32(haystack, 0)) == 0x00000417 && |
185 | ntohl(get_u32(haystack, 4)) == 0x27101980) | |
c237fe24 JE |
186 | return IPP2P_BIT * 100 + 50; |
187 | break; | |
c66d291e CB |
188 | case 36: |
189 | if (get_u32(haystack, 8) == __constant_htonl(0x00000400) && | |
190 | get_u32(haystack, 28) == __constant_htonl(0x00000104)) | |
c237fe24 | 191 | return IPP2P_BIT * 100 + 51; |
c66d291e | 192 | if (get_u32(haystack, 8) == __constant_htonl(0x00000400)) |
c237fe24 JE |
193 | return IPP2P_BIT * 100 + 61; |
194 | break; | |
c66d291e CB |
195 | case 57: |
196 | if (get_u32(haystack, 8) == __constant_htonl(0x00000404) && | |
197 | get_u32(haystack, 28) == __constant_htonl(0x00000104)) | |
c237fe24 | 198 | return IPP2P_BIT * 100 + 52; |
c66d291e | 199 | if (get_u32(haystack, 8) == __constant_htonl(0x00000404)) |
c237fe24 JE |
200 | return IPP2P_BIT * 100 + 62; |
201 | break; | |
c66d291e CB |
202 | case 59: |
203 | if (get_u32(haystack, 8) == __constant_htonl(0x00000406) && | |
204 | get_u32(haystack, 28) == __constant_htonl(0x00000104)) | |
c237fe24 | 205 | return (IPP2P_BIT * 100 + 53); |
c66d291e | 206 | if (get_u32(haystack, 8) == __constant_htonl(0x00000406)) |
c237fe24 JE |
207 | return (IPP2P_BIT * 100 + 63); |
208 | break; | |
c66d291e CB |
209 | case 203: |
210 | if (get_u32(haystack, 0) == __constant_htonl(0x00000405)) | |
c237fe24 JE |
211 | return IPP2P_BIT * 100 + 54; |
212 | break; | |
c66d291e CB |
213 | case 21: |
214 | if (get_u32(haystack, 0) == __constant_htonl(0x00000401)) | |
c237fe24 JE |
215 | return IPP2P_BIT * 100 + 55; |
216 | break; | |
c66d291e CB |
217 | case 44: |
218 | if (get_u32(haystack, 0) == __constant_htonl(0x00000827) && | |
219 | get_u32(haystack, 4) == __constant_htonl(0x37502950)) | |
c237fe24 JE |
220 | return IPP2P_BIT * 100 + 80; |
221 | break; | |
222 | default: | |
223 | /* this packet does not have a constant size */ | |
c66d291e CB |
224 | if (packet_len >= 32 && |
225 | get_u32(haystack, 8) == __constant_htonl(0x00000402) && | |
226 | get_u32(haystack, 28) == __constant_htonl(0x00000104)) | |
c237fe24 JE |
227 | return IPP2P_BIT * 100 + 56; |
228 | break; | |
229 | } | |
230 | ||
231 | /* some extra-bitcomet rules: "d1:" [a|r] "d2:id20:" */ | |
c66d291e CB |
232 | if (packet_len > 22 && get_u8(haystack, 0) == 'd' && |
233 | get_u8(haystack, 1) == '1' && get_u8(haystack, 2) == ':') | |
234 | if (get_u8(haystack, 3) == 'a' || | |
235 | get_u8(haystack, 3) == 'r') | |
236 | if (memcmp(haystack + 4, "d2:id20:", 8) == 0) | |
c237fe24 JE |
237 | return IPP2P_BIT * 100 + 57; |
238 | ||
44d6f47a JE |
239 | #if 0 |
240 | /* bitlord rules */ | |
c66d291e | 241 | /* packetlen must be bigger than 32 */ |
44d6f47a | 242 | /* first 4 bytes are zero */ |
c66d291e | 243 | if (packet_len > 32 && get_u32(haystack, 0) == 0x00000000) { |
44d6f47a | 244 | /* first rule: 00 00 00 00 01 00 00 xx xx xx xx 00 00 00 00*/ |
c66d291e CB |
245 | if (get_u32(haystack, 4) == 0x00000000 && |
246 | get_u32(haystack, 8) == 0x00010000 && | |
247 | get_u32(haystack, 16) == 0x00000000) | |
c237fe24 JE |
248 | return IPP2P_BIT * 100 + 71; |
249 | ||
44d6f47a | 250 | /* 00 01 00 00 0d 00 00 xx xx xx xx 00 00 00 00*/ |
c66d291e CB |
251 | if (get_u32(haystack, 4) == 0x00000001 && |
252 | get_u32(haystack, 8) == 0x000d0000 && | |
253 | get_u32(haystack, 16) == 0x00000000) | |
c237fe24 | 254 | return IPP2P_BIT * 100 + 71; |
44d6f47a JE |
255 | } |
256 | #endif | |
257 | ||
c237fe24 JE |
258 | return 0; |
259 | } | |
44d6f47a | 260 | |
c237fe24 | 261 | /* Search for Ares commands */ |
569643ac JE |
262 | static unsigned int |
263 | search_ares(const unsigned char *payload, const unsigned int plen) | |
44d6f47a | 264 | { |
17a03128 CB |
265 | if (plen < 3) |
266 | return 0; | |
44d6f47a | 267 | /* all ares packets start with */ |
c237fe24 JE |
268 | if (payload[1] == 0 && plen - payload[0] == 3) { |
269 | switch (payload[2]) { | |
270 | case 0x5a: | |
271 | /* ares connect */ | |
272 | if (plen == 6 && payload[5] == 0x05) | |
273 | return IPP2P_ARES * 100 + 1; | |
274 | break; | |
275 | case 0x09: | |
276 | /* | |
277 | * ares search, min 3 chars --> 14 bytes | |
278 | * lets define a search can be up to 30 chars | |
279 | * --> max 34 bytes | |
280 | */ | |
281 | if (plen >= 14 && plen <= 34) | |
282 | return IPP2P_ARES * 100 + 1; | |
283 | break; | |
44d6f47a | 284 | #ifdef IPP2P_DEBUG_ARES |
c237fe24 JE |
285 | default: |
286 | printk(KERN_DEBUG "Unknown Ares command %x " | |
287 | "recognized, len: %u\n", | |
288 | (unsigned int)payload[2], plen); | |
289 | #endif | |
44d6f47a JE |
290 | } |
291 | } | |
292 | ||
c237fe24 | 293 | #if 0 |
44d6f47a JE |
294 | /* found connect packet: 03 00 5a 04 03 05 */ |
295 | /* new version ares 1.8: 03 00 5a xx xx 05 */ | |
c237fe24 JE |
296 | if (plen == 6) |
297 | /* possible connect command */ | |
298 | if (payload[0] == 0x03 && payload[1] == 0x00 && | |
299 | payload[2] == 0x5a && payload[5] == 0x05) | |
300 | return IPP2P_ARES * 100 + 1; | |
301 | ||
302 | if (plen == 60) | |
303 | /* possible download command*/ | |
304 | if (payload[59] == 0x0a && payload[58] == 0x0a) | |
305 | if (memcmp(t, "PUSH SHA1:", 10) == 0) | |
306 | /* found download command */ | |
307 | return IPP2P_ARES * 100 + 2; | |
44d6f47a JE |
308 | #endif |
309 | ||
c237fe24 JE |
310 | return 0; |
311 | } | |
44d6f47a | 312 | |
c237fe24 | 313 | /* Search for SoulSeek commands */ |
569643ac JE |
314 | static unsigned int |
315 | search_soul(const unsigned char *payload, const unsigned int plen) | |
44d6f47a | 316 | { |
17a03128 CB |
317 | if (plen < 8) |
318 | return 0; | |
c237fe24 JE |
319 | /* match: xx xx xx xx | xx = sizeof(payload) - 4 */ |
320 | if (get_u32(payload, 0) == plen - 4) { | |
569643ac JE |
321 | const uint32_t m = get_u32(payload, 4); |
322 | ||
c237fe24 JE |
323 | /* match 00 yy yy 00, yy can be everything */ |
324 | if (get_u8(payload, 4) == 0x00 && get_u8(payload, 7) == 0x00) { | |
44d6f47a | 325 | #ifdef IPP2P_DEBUG_SOUL |
c237fe24 JE |
326 | printk(KERN_DEBUG "0: Soulseek command 0x%x " |
327 | "recognized\n", get_u32(payload, 4)); | |
328 | #endif | |
329 | return IPP2P_SOUL * 100 + 1; | |
330 | } | |
331 | ||
332 | /* next match: 01 yy 00 00 | yy can be everything */ | |
333 | if (get_u8(payload, 4) == 0x01 && get_u16(payload, 6) == 0x0000) { | |
44d6f47a | 334 | #ifdef IPP2P_DEBUG_SOUL |
c237fe24 JE |
335 | printk(KERN_DEBUG "1: Soulseek command 0x%x " |
336 | "recognized\n", get_u16(payload, 4)); | |
337 | #endif | |
338 | return IPP2P_SOUL * 100 + 2; | |
339 | } | |
340 | ||
341 | /* other soulseek commandos are: 1-5,7,9,13-18,22,23,26,28,35-37,40-46,50,51,60,62-69,91,92,1001 */ | |
342 | /* try to do this in an intelligent way */ | |
343 | /* get all small commandos */ | |
344 | switch (m) { | |
44d6f47a JE |
345 | case 7: |
346 | case 9: | |
347 | case 22: | |
348 | case 23: | |
349 | case 26: | |
350 | case 28: | |
351 | case 50: | |
352 | case 51: | |
353 | case 60: | |
354 | case 91: | |
355 | case 92: | |
356 | case 1001: | |
357 | #ifdef IPP2P_DEBUG_SOUL | |
c237fe24 JE |
358 | printk(KERN_DEBUG "2: Soulseek command 0x%x " |
359 | "recognized\n", get_u16(payload, 4)); | |
360 | #endif | |
361 | return IPP2P_SOUL * 100 + 3; | |
362 | } | |
363 | ||
364 | if (m > 0 && m < 6) { | |
44d6f47a | 365 | #ifdef IPP2P_DEBUG_SOUL |
c237fe24 JE |
366 | printk(KERN_DEBUG "3: Soulseek command 0x%x " |
367 | "recognized\n", get_u16(payload, 4)); | |
368 | #endif | |
369 | return IPP2P_SOUL * 100 + 4; | |
370 | } | |
371 | ||
372 | if (m > 12 && m < 19) { | |
44d6f47a | 373 | #ifdef IPP2P_DEBUG_SOUL |
c237fe24 JE |
374 | printk(KERN_DEBUG "4: Soulseek command 0x%x " |
375 | "recognized\n", get_u16(payload, 4)); | |
376 | #endif | |
377 | return IPP2P_SOUL * 100 + 5; | |
378 | } | |
44d6f47a | 379 | |
c237fe24 | 380 | if (m > 34 && m < 38) { |
44d6f47a | 381 | #ifdef IPP2P_DEBUG_SOUL |
c237fe24 JE |
382 | printk(KERN_DEBUG "5: Soulseek command 0x%x " |
383 | "recognized\n", get_u16(payload, 4)); | |
384 | #endif | |
385 | return IPP2P_SOUL * 100 + 6; | |
386 | } | |
44d6f47a | 387 | |
c237fe24 | 388 | if (m > 39 && m < 47) { |
44d6f47a | 389 | #ifdef IPP2P_DEBUG_SOUL |
c237fe24 JE |
390 | printk(KERN_DEBUG "6: Soulseek command 0x%x " |
391 | "recognized\n", get_u16(payload, 4)); | |
392 | #endif | |
393 | return IPP2P_SOUL * 100 + 7; | |
394 | } | |
44d6f47a | 395 | |
c237fe24 | 396 | if (m > 61 && m < 70) { |
44d6f47a | 397 | #ifdef IPP2P_DEBUG_SOUL |
c237fe24 JE |
398 | printk(KERN_DEBUG "7: Soulseek command 0x%x " |
399 | "recognized\n", get_u16(payload, 4)); | |
400 | #endif | |
401 | return IPP2P_SOUL * 100 + 8; | |
402 | } | |
44d6f47a JE |
403 | |
404 | #ifdef IPP2P_DEBUG_SOUL | |
c237fe24 JE |
405 | printk(KERN_DEBUG "unknown SOULSEEK command: 0x%x, first " |
406 | "16 bit: 0x%x, first 8 bit: 0x%x ,soulseek ???\n", | |
407 | get_u32(payload, 4), get_u16(payload, 4) >> 16, | |
408 | get_u8(payload, 4) >> 24); | |
409 | #endif | |
410 | } | |
411 | ||
44d6f47a JE |
412 | /* match 14 00 00 00 01 yy 00 00 00 STRING(YY) 01 00 00 00 00 46|50 00 00 00 00 */ |
413 | /* without size at the beginning !!! */ | |
c237fe24 | 414 | if (get_u32(payload, 0) == 0x14 && get_u8(payload, 4) == 0x01) { |
569643ac JE |
415 | uint32_t y = get_u32(payload, 5); |
416 | ||
44d6f47a | 417 | /* we need 19 chars + string */ |
c237fe24 JE |
418 | if (y + 19 <= plen) { |
419 | const unsigned char *w = payload + 9 + y; | |
420 | if (get_u32(w, 0) == 0x01 && | |
421 | (get_u16(w, 4) == 0x4600 || | |
422 | get_u16(w, 4) == 0x5000) && | |
423 | get_u32(w, 6) == 0x00) | |
424 | ; | |
44d6f47a JE |
425 | #ifdef IPP2P_DEBUG_SOUL |
426 | printk(KERN_DEBUG "Soulssek special client command recognized\n"); | |
c237fe24 JE |
427 | #endif |
428 | return IPP2P_SOUL * 100 + 9; | |
44d6f47a JE |
429 | } |
430 | } | |
c237fe24 | 431 | return 0; |
44d6f47a JE |
432 | } |
433 | ||
c237fe24 | 434 | /* Search for WinMX commands */ |
569643ac JE |
435 | static unsigned int |
436 | search_winmx(const unsigned char *payload, const unsigned int plen) | |
44d6f47a | 437 | { |
c237fe24 JE |
438 | if (plen == 4 && memcmp(payload, "SEND", 4) == 0) |
439 | return IPP2P_WINMX * 100 + 1; | |
440 | if (plen == 3 && memcmp(payload, "GET", 3) == 0) | |
441 | return IPP2P_WINMX * 100 + 2; | |
442 | /* | |
443 | if (packet_len < head_len + 10) | |
444 | return 0; | |
445 | */ | |
446 | if (plen < 10) | |
447 | return 0; | |
448 | ||
449 | if (memcmp(payload, "SEND", 4) == 0 || memcmp(payload, "GET", 3) == 0) { | |
569643ac JE |
450 | uint16_t c = 4; |
451 | const uint16_t end = plen - 2; | |
452 | uint8_t count = 0; | |
c237fe24 JE |
453 | |
454 | while (c < end) { | |
455 | if (payload[c] == 0x20 && payload[c+1] == 0x22) { | |
456 | c++; | |
457 | count++; | |
458 | if (count >= 2) | |
459 | return IPP2P_WINMX * 100 + 3; | |
460 | } | |
461 | c++; | |
462 | } | |
463 | } | |
464 | ||
465 | if (plen == 149 && payload[0] == '8') { | |
44d6f47a | 466 | #ifdef IPP2P_DEBUG_WINMX |
c237fe24 | 467 | printk(KERN_INFO "maybe WinMX\n"); |
44d6f47a | 468 | #endif |
c237fe24 JE |
469 | if (get_u32(payload, 17) == 0 && get_u32(payload, 21) == 0 && |
470 | get_u32(payload, 25) == 0 && | |
471 | // get_u32(payload, 33) == __constant_htonl(0x71182b1a) && | |
472 | // get_u32(payload, 37) == __constant_htonl(0x05050000) && | |
473 | // get_u32(payload, 133) == __constant_htonl(0x31097edf) && | |
474 | // get_u32(payload, 145) == __constant_htonl(0xdcb8f792)) | |
475 | get_u16(payload, 39) == 0 && | |
476 | get_u16(payload, 135) == __constant_htons(0x7edf) && | |
477 | get_u16(payload,147) == __constant_htons(0xf792)) | |
478 | { | |
44d6f47a | 479 | #ifdef IPP2P_DEBUG_WINMX |
c237fe24 | 480 | printk(KERN_INFO "got WinMX\n"); |
44d6f47a | 481 | #endif |
c237fe24 JE |
482 | return IPP2P_WINMX * 100 + 4; |
483 | } | |
484 | } | |
485 | return 0; | |
486 | } | |
487 | ||
488 | /* Search for appleJuice commands */ | |
569643ac JE |
489 | static unsigned int |
490 | search_apple(const unsigned char *payload, const unsigned int plen) | |
44d6f47a | 491 | { |
c237fe24 JE |
492 | if (plen > 7 && payload[6] == 0x0d && payload[7] == 0x0a && |
493 | memcmp(payload, "ajprot", 6) == 0) | |
494 | return IPP2P_APPLE * 100; | |
495 | ||
496 | return 0; | |
44d6f47a JE |
497 | } |
498 | ||
c237fe24 | 499 | /* Search for BitTorrent commands */ |
569643ac JE |
500 | static unsigned int |
501 | search_bittorrent(const unsigned char *payload, const unsigned int plen) | |
44d6f47a | 502 | { |
c237fe24 JE |
503 | if (plen > 20) { |
504 | /* test for match 0x13+"BitTorrent protocol" */ | |
505 | if (payload[0] == 0x13) | |
506 | if (memcmp(payload + 1, "BitTorrent protocol", 19) == 0) | |
507 | return IPP2P_BIT * 100; | |
c237fe24 | 508 | /* |
43864ac6 TT |
509 | * Any tracker command starts with GET / then *may be* some file on web server |
510 | * (e.g. announce.php or dupa.pl or whatever.cgi or NOTHING for tracker on root dir) | |
511 | * but *must have* one (or more) of strings listed below (true for scrape and announce) | |
c237fe24 JE |
512 | */ |
513 | if (memcmp(payload, "GET /", 5) == 0) { | |
43864ac6 | 514 | if (HX_memmem(payload, plen, "info_hash=", 9) != NULL) |
c237fe24 | 515 | return IPP2P_BIT * 100 + 1; |
43864ac6 | 516 | if (HX_memmem(payload, plen, "peer_id=", 8) != NULL) |
c237fe24 | 517 | return IPP2P_BIT * 100 + 2; |
43864ac6 TT |
518 | if (HX_memmem(payload, plen, "passkey=", 8) != NULL) |
519 | return IPP2P_BIT * 100 + 4; | |
c237fe24 JE |
520 | } |
521 | } else { | |
522 | /* bitcomet encryptes the first packet, so we have to detect another | |
523 | * one later in the flow */ | |
304e5e52 | 524 | /* first try failed, too many false positives */ |
c237fe24 JE |
525 | /* |
526 | if (size == 5 && get_u32(t, 0) == __constant_htonl(1) && | |
527 | t[4] < 3) | |
528 | return IPP2P_BIT * 100 + 3; | |
529 | */ | |
530 | ||
531 | /* second try: block request packets */ | |
532 | if (plen == 17 && | |
533 | get_u32(payload, 0) == __constant_htonl(0x0d) && | |
534 | payload[4] == 0x06 && | |
535 | get_u32(payload,13) == __constant_htonl(0x4000)) | |
536 | return IPP2P_BIT * 100 + 3; | |
44d6f47a | 537 | } |
c237fe24 JE |
538 | |
539 | return 0; | |
44d6f47a JE |
540 | } |
541 | ||
c237fe24 | 542 | /* check for Kazaa get command */ |
569643ac JE |
543 | static unsigned int |
544 | search_kazaa(const unsigned char *payload, const unsigned int plen) | |
44d6f47a | 545 | { |
17a03128 CB |
546 | if (plen < 13) |
547 | return 0; | |
c237fe24 JE |
548 | if (payload[plen-2] == 0x0d && payload[plen-1] == 0x0a && |
549 | memcmp(payload, "GET /.hash=", 11) == 0) | |
550 | return IPP2P_DATA_KAZAA * 100; | |
44d6f47a | 551 | |
c237fe24 | 552 | return 0; |
44d6f47a JE |
553 | } |
554 | ||
c237fe24 | 555 | /* check for gnutella get command */ |
569643ac JE |
556 | static unsigned int |
557 | search_gnu(const unsigned char *payload, const unsigned int plen) | |
44d6f47a | 558 | { |
17a03128 CB |
559 | if (plen < 11) |
560 | return 0; | |
c237fe24 JE |
561 | if (payload[plen-2] == 0x0d && payload[plen-1] == 0x0a) { |
562 | if (memcmp(payload, "GET /get/", 9) == 0) | |
563 | return IPP2P_DATA_GNU * 100 + 1; | |
304e5e52 | 564 | if (plen >= 15 && memcmp(payload, "GET /uri-res/", 13) == 0) |
c237fe24 JE |
565 | return IPP2P_DATA_GNU * 100 + 2; |
566 | } | |
567 | return 0; | |
44d6f47a JE |
568 | } |
569 | ||
c237fe24 | 570 | /* check for gnutella get commands and other typical data */ |
569643ac JE |
571 | static unsigned int |
572 | search_all_gnu(const unsigned char *payload, const unsigned int plen) | |
44d6f47a | 573 | { |
17a03128 CB |
574 | if (plen < 11) |
575 | return 0; | |
c237fe24 | 576 | if (payload[plen-2] == 0x0d && payload[plen-1] == 0x0a) { |
304e5e52 | 577 | if (plen >= 19 && memcmp(payload, "GNUTELLA CONNECT/", 17) == 0) |
c237fe24 | 578 | return IPP2P_GNU * 100 + 1; |
304e5e52 | 579 | if (memcmp(payload, "GNUTELLA/", 9) == 0) |
c237fe24 JE |
580 | return IPP2P_GNU * 100 + 2; |
581 | ||
17a03128 CB |
582 | if (plen >= 22 && (memcmp(payload, "GET /get/", 9) == 0 || |
583 | memcmp(payload, "GET /uri-res/", 13) == 0)) | |
c237fe24 | 584 | { |
17a03128 | 585 | unsigned int c; |
569643ac | 586 | |
17a03128 | 587 | for (c = 0; c < plen - 22; ++c) |
3f7288ab CB |
588 | if (payload[c] == 0x0d && |
589 | payload[c+1] == 0x0a && | |
c237fe24 JE |
590 | (memcmp(&payload[c+2], "X-Gnutella-", 11) == 0 || |
591 | memcmp(&payload[c+2], "X-Queue:", 8) == 0)) | |
592 | return IPP2P_GNU * 100 + 3; | |
44d6f47a JE |
593 | } |
594 | } | |
c237fe24 | 595 | return 0; |
44d6f47a JE |
596 | } |
597 | ||
c237fe24 | 598 | /* check for KaZaA download commands and other typical data */ |
d01a5f3d | 599 | /* plen is guaranteed to be >= 5 (see @matchlist) */ |
569643ac JE |
600 | static unsigned int |
601 | search_all_kazaa(const unsigned char *payload, const unsigned int plen) | |
44d6f47a | 602 | { |
d01a5f3d JE |
603 | uint16_t c, end, rem; |
604 | ||
17a03128 | 605 | if (plen < 7) |
879e964f JE |
606 | /* too short for anything we test for - early bailout */ |
607 | return 0; | |
608 | ||
22db3bcb JE |
609 | if (payload[plen-2] != 0x0d || payload[plen-1] != 0x0a) |
610 | return 0; | |
c237fe24 | 611 | |
22db3bcb JE |
612 | if (memcmp(payload, "GIVE ", 5) == 0) |
613 | return IPP2P_KAZAA * 100 + 1; | |
614 | ||
d01a5f3d JE |
615 | if (memcmp(payload, "GET /", 5) != 0) |
616 | return 0; | |
617 | ||
4cdfd496 JE |
618 | if (plen < 18) |
619 | /* The next tests would not succeed anyhow. */ | |
620 | return 0; | |
621 | ||
d01a5f3d JE |
622 | end = plen - 18; |
623 | rem = plen - 5; | |
624 | for (c = 5; c < end; ++c, --rem) { | |
625 | if (payload[c] != 0x0d) | |
626 | continue; | |
627 | if (payload[c+1] != 0x0a) | |
628 | continue; | |
629 | if (rem >= 18 && | |
630 | memcmp(&payload[c+2], "X-Kazaa-Username: ", 18) == 0) | |
631 | return IPP2P_KAZAA * 100 + 2; | |
632 | if (rem >= 24 && | |
633 | memcmp(&payload[c+2], "User-Agent: PeerEnabler/", 24) == 0) | |
634 | return IPP2P_KAZAA * 100 + 2; | |
44d6f47a | 635 | } |
22db3bcb | 636 | |
c237fe24 | 637 | return 0; |
44d6f47a JE |
638 | } |
639 | ||
c237fe24 | 640 | /* fast check for edonkey file segment transfer command */ |
569643ac JE |
641 | static unsigned int |
642 | search_edk(const unsigned char *payload, const unsigned int plen) | |
44d6f47a | 643 | { |
17a03128 CB |
644 | if (plen < 6) |
645 | return 0; | |
c237fe24 JE |
646 | if (payload[0] != 0xe3) { |
647 | return 0; | |
648 | } else { | |
649 | if (payload[5] == 0x47) | |
650 | return IPP2P_DATA_EDK * 100; | |
651 | else | |
652 | return 0; | |
653 | } | |
44d6f47a JE |
654 | } |
655 | ||
c237fe24 | 656 | /* intensive but slower search for some edonkey packets including size-check */ |
569643ac JE |
657 | static unsigned int |
658 | search_all_edk(const unsigned char *payload, const unsigned int plen) | |
44d6f47a | 659 | { |
17a03128 CB |
660 | if (plen < 6) |
661 | return 0; | |
c237fe24 JE |
662 | if (payload[0] != 0xe3) { |
663 | return 0; | |
664 | } else { | |
17a03128 | 665 | unsigned int cmd = get_u16(payload, 1); |
c237fe24 JE |
666 | |
667 | if (cmd == plen - 5) { | |
668 | switch (payload[5]) { | |
669 | case 0x01: | |
670 | /* Client: hello or Server:hello */ | |
671 | return IPP2P_EDK * 100 + 1; | |
672 | case 0x4c: | |
673 | /* Client: Hello-Answer */ | |
674 | return IPP2P_EDK * 100 + 9; | |
675 | } | |
676 | } | |
677 | return 0; | |
44d6f47a | 678 | } |
44d6f47a JE |
679 | } |
680 | ||
c237fe24 | 681 | /* fast check for Direct Connect send command */ |
569643ac JE |
682 | static unsigned int |
683 | search_dc(const unsigned char *payload, const unsigned int plen) | |
44d6f47a | 684 | { |
17a03128 CB |
685 | if (plen < 6) |
686 | return 0; | |
c237fe24 JE |
687 | if (payload[0] != 0x24) { |
688 | return 0; | |
689 | } else { | |
690 | if (memcmp(&payload[1], "Send|", 5) == 0) | |
691 | return IPP2P_DATA_DC * 100; | |
692 | else | |
693 | return 0; | |
694 | } | |
44d6f47a JE |
695 | } |
696 | ||
c237fe24 | 697 | /* intensive but slower check for all direct connect packets */ |
569643ac JE |
698 | static unsigned int |
699 | search_all_dc(const unsigned char *payload, const unsigned int plen) | |
44d6f47a | 700 | { |
17a03128 CB |
701 | if (plen < 7) |
702 | return 0; | |
c237fe24 JE |
703 | if (payload[0] == 0x24 && payload[plen-1] == 0x7c) { |
704 | const unsigned char *t = &payload[1]; | |
705 | ||
706 | /* Client-Hub-Protocol */ | |
707 | if (memcmp(t, "Lock ", 5) == 0) | |
708 | return IPP2P_DC * 100 + 1; | |
709 | ||
710 | /* | |
711 | * Client-Client-Protocol, some are already recognized by | |
712 | * client-hub (like lock) | |
713 | */ | |
17a03128 | 714 | if (plen >= 9 && memcmp(t, "MyNick ", 7) == 0) |
c237fe24 JE |
715 | return IPP2P_DC * 100 + 38; |
716 | } | |
717 | return 0; | |
44d6f47a JE |
718 | } |
719 | ||
c237fe24 | 720 | /* check for mute */ |
569643ac JE |
721 | static unsigned int |
722 | search_mute(const unsigned char *payload, const unsigned int plen) | |
44d6f47a | 723 | { |
c237fe24 JE |
724 | if (plen == 209 || plen == 345 || plen == 473 || plen == 609 || |
725 | plen == 1121) { | |
726 | //printk(KERN_DEBUG "size hit: %u", size); | |
727 | if (memcmp(payload,"PublicKey: ", 11) == 0) { | |
728 | return IPP2P_MUTE * 100 + 0; | |
729 | /* | |
730 | if (memcmp(t + size - 14, "\x0aEndPublicKey\x0a", 14) == 0) | |
731 | printk(KERN_DEBUG "end pubic key hit: %u", size); | |
732 | */ | |
44d6f47a JE |
733 | } |
734 | } | |
735 | return 0; | |
736 | } | |
737 | ||
44d6f47a | 738 | /* check for xdcc */ |
569643ac JE |
739 | static unsigned int |
740 | search_xdcc(const unsigned char *payload, const unsigned int plen) | |
44d6f47a JE |
741 | { |
742 | /* search in small packets only */ | |
c237fe24 JE |
743 | if (plen > 20 && plen < 200 && payload[plen-1] == 0x0a && |
744 | payload[plen-2] == 0x0d && memcmp(payload, "PRIVMSG ", 8) == 0) | |
44d6f47a | 745 | { |
569643ac JE |
746 | uint16_t x = 10; |
747 | const uint16_t end = plen - 13; | |
c237fe24 JE |
748 | |
749 | /* | |
750 | * is seems to be a irc private massage, chedck for | |
751 | * xdcc command | |
752 | */ | |
753 | while (x < end) { | |
44d6f47a | 754 | if (payload[x] == ':') |
c237fe24 JE |
755 | if (memcmp(&payload[x+1], "xdcc send #", 11) == 0) |
756 | return IPP2P_XDCC * 100 + 0; | |
44d6f47a JE |
757 | x++; |
758 | } | |
759 | } | |
760 | return 0; | |
761 | } | |
762 | ||
763 | /* search for waste */ | |
569643ac JE |
764 | static unsigned int |
765 | search_waste(const unsigned char *payload, const unsigned int plen) | |
44d6f47a | 766 | { |
c237fe24 JE |
767 | if (plen >= 8 && memcmp(payload, "GET.sha1:", 9) == 0) |
768 | return IPP2P_WASTE * 100 + 0; | |
44d6f47a JE |
769 | |
770 | return 0; | |
771 | } | |
772 | ||
569643ac JE |
773 | static const struct { |
774 | unsigned int command; | |
569643ac JE |
775 | unsigned int packet_len; |
776 | unsigned int (*function_name)(const unsigned char *, const unsigned int); | |
44d6f47a | 777 | } matchlist[] = { |
a1d307e3 | 778 | {IPP2P_EDK, 20, search_all_edk}, |
01df89eb JE |
779 | {IPP2P_DATA_KAZAA, 200, search_kazaa}, /* exp */ |
780 | {IPP2P_DATA_EDK, 60, search_edk}, /* exp */ | |
781 | {IPP2P_DATA_DC, 26, search_dc}, /* exp */ | |
a1d307e3 | 782 | {IPP2P_DC, 5, search_all_dc}, |
01df89eb | 783 | {IPP2P_DATA_GNU, 40, search_gnu}, /* exp */ |
a1d307e3 JE |
784 | {IPP2P_GNU, 5, search_all_gnu}, |
785 | {IPP2P_KAZAA, 5, search_all_kazaa}, | |
786 | {IPP2P_BIT, 20, search_bittorrent}, | |
787 | {IPP2P_APPLE, 5, search_apple}, | |
788 | {IPP2P_SOUL, 5, search_soul}, | |
789 | {IPP2P_WINMX, 2, search_winmx}, | |
790 | {IPP2P_ARES, 5, search_ares}, | |
791 | {IPP2P_MUTE, 200, search_mute}, | |
792 | {IPP2P_WASTE, 5, search_waste}, | |
793 | {IPP2P_XDCC, 5, search_xdcc}, | |
c237fe24 | 794 | {0}, |
44d6f47a JE |
795 | }; |
796 | ||
569643ac JE |
797 | static const struct { |
798 | unsigned int command; | |
569643ac JE |
799 | unsigned int packet_len; |
800 | unsigned int (*function_name)(const unsigned char *, const unsigned int); | |
44d6f47a | 801 | } udp_list[] = { |
a1d307e3 JE |
802 | {IPP2P_KAZAA, 14, udp_search_kazaa}, |
803 | {IPP2P_BIT, 23, udp_search_bit}, | |
804 | {IPP2P_GNU, 11, udp_search_gnu}, | |
805 | {IPP2P_EDK, 9, udp_search_edk}, | |
806 | {IPP2P_DC, 12, udp_search_directconnect}, | |
c237fe24 | 807 | {0}, |
44d6f47a JE |
808 | }; |
809 | ||
cc23d0a2 | 810 | static bool |
9a18a05d | 811 | ipp2p_mt(const struct sk_buff *skb, struct xt_action_param *par) |
44d6f47a | 812 | { |
ee7e4f5a | 813 | const struct ipt_p2p_info *info = par->matchinfo; |
569643ac JE |
814 | const unsigned char *haystack; |
815 | const struct iphdr *ip = ip_hdr(skb); | |
816 | bool p2p_result = false; | |
817 | int i = 0; | |
3c8131b9 | 818 | unsigned int hlen = ntohs(ip->tot_len) - ip_hdrlen(skb); /* hlen = packet-data length */ |
c237fe24 JE |
819 | |
820 | /* must not be a fragment */ | |
ee7e4f5a | 821 | if (par->fragoff != 0) { |
c237fe24 | 822 | if (info->debug) |
ee7e4f5a | 823 | printk("IPP2P.match: offset found %d\n", par->fragoff); |
c237fe24 JE |
824 | return 0; |
825 | } | |
826 | ||
827 | /* make sure that skb is linear */ | |
828 | if (skb_is_nonlinear(skb)) { | |
829 | if (info->debug) | |
830 | printk("IPP2P.match: nonlinear skb found\n"); | |
831 | return 0; | |
832 | } | |
44d6f47a | 833 | |
3c8131b9 | 834 | haystack = skb_network_header(skb) + ip_hdrlen(skb); |
44d6f47a | 835 | |
c237fe24 JE |
836 | switch (ip->protocol) { |
837 | case IPPROTO_TCP: /* what to do with a TCP packet */ | |
44d6f47a | 838 | { |
fbbca687 | 839 | const struct tcphdr *tcph = (const void *)ip + ip_hdrlen(skb); |
c237fe24 JE |
840 | |
841 | if (tcph->fin) return 0; /* if FIN bit is set bail out */ | |
842 | if (tcph->syn) return 0; /* if SYN bit is set bail out */ | |
843 | if (tcph->rst) return 0; /* if RST bit is set bail out */ | |
844 | ||
845 | haystack += tcph->doff * 4; /* get TCP-Header-Size */ | |
430723ec JE |
846 | if (tcph->doff * 4 > hlen) { |
847 | if (info->debug) | |
848 | pr_info("TCP header indicated packet larger than it is\n"); | |
849 | hlen = 0; | |
850 | } else { | |
851 | hlen -= tcph->doff * 4; | |
852 | } | |
c237fe24 | 853 | while (matchlist[i].command) { |
a1d307e3 | 854 | if ((info->cmd & matchlist[i].command) == matchlist[i].command && |
c237fe24 JE |
855 | hlen > matchlist[i].packet_len) |
856 | { | |
857 | p2p_result = matchlist[i].function_name(haystack, hlen); | |
858 | if (p2p_result) { | |
859 | if (info->debug) | |
860 | printk("IPP2P.debug:TCP-match: %i from: %u.%u.%u.%u:%i to: %u.%u.%u.%u:%i Length: %i\n", | |
861 | p2p_result, NIPQUAD(ip->saddr),ntohs(tcph->source), NIPQUAD(ip->daddr),ntohs(tcph->dest),hlen); | |
862 | return p2p_result; | |
863 | } | |
864 | } | |
865 | i++; | |
866 | } | |
867 | return p2p_result; | |
44d6f47a | 868 | } |
c237fe24 JE |
869 | |
870 | case IPPROTO_UDP: /* what to do with an UDP packet */ | |
44d6f47a | 871 | { |
fbbca687 | 872 | const struct udphdr *udph = (const void *)ip + ip_hdrlen(skb); |
c237fe24 | 873 | |
c66d291e CB |
874 | haystack += sizeof(*udph); |
875 | if (sizeof(*udph) > hlen) { | |
876 | if (info->debug) | |
877 | pr_info("UDP header indicated packet larger than it is\n"); | |
878 | hlen = 0; | |
879 | } else { | |
880 | hlen -= sizeof(*udph); | |
881 | } | |
882 | ||
c237fe24 | 883 | while (udp_list[i].command) { |
a1d307e3 | 884 | if ((info->cmd & udp_list[i].command) == udp_list[i].command && |
c237fe24 JE |
885 | hlen > udp_list[i].packet_len) |
886 | { | |
887 | p2p_result = udp_list[i].function_name(haystack, hlen); | |
888 | if (p2p_result) { | |
889 | if (info->debug) | |
890 | printk("IPP2P.debug:UDP-match: %i from: %u.%u.%u.%u:%i to: %u.%u.%u.%u:%i Length: %i\n", | |
891 | p2p_result, NIPQUAD(ip->saddr), ntohs(udph->source), NIPQUAD(ip->daddr), ntohs(udph->dest), hlen); | |
892 | return p2p_result; | |
893 | } | |
894 | } | |
895 | i++; | |
44d6f47a | 896 | } |
c237fe24 JE |
897 | return p2p_result; |
898 | } | |
899 | ||
900 | default: | |
901 | return 0; | |
44d6f47a | 902 | } |
44d6f47a JE |
903 | } |
904 | ||
cc23d0a2 | 905 | static struct xt_match ipp2p_mt_reg __read_mostly = { |
c237fe24 | 906 | .name = "ipp2p", |
137ecb98 | 907 | .revision = 1, |
be6fbee5 | 908 | .family = NFPROTO_IPV4, |
cc23d0a2 | 909 | .match = ipp2p_mt, |
c237fe24 JE |
910 | .matchsize = sizeof(struct ipt_p2p_info), |
911 | .me = THIS_MODULE, | |
44d6f47a JE |
912 | }; |
913 | ||
cc23d0a2 | 914 | static int __init ipp2p_mt_init(void) |
44d6f47a | 915 | { |
cc23d0a2 | 916 | return xt_register_match(&ipp2p_mt_reg); |
44d6f47a | 917 | } |
c237fe24 | 918 | |
cc23d0a2 | 919 | static void __exit ipp2p_mt_exit(void) |
44d6f47a | 920 | { |
cc23d0a2 | 921 | xt_unregister_match(&ipp2p_mt_reg); |
44d6f47a | 922 | } |
c237fe24 | 923 | |
cc23d0a2 JE |
924 | module_init(ipp2p_mt_init); |
925 | module_exit(ipp2p_mt_exit); | |
29139c14 | 926 | MODULE_ALIAS("ipt_ipp2p"); |