]>
Commit | Line | Data |
---|---|---|
30ba7eb4 | 1 | #include <linux/gfp.h> |
44d6f47a | 2 | #include <linux/module.h> |
30ba7eb4 | 3 | #include <linux/textsearch.h> |
44d6f47a JE |
4 | #include <linux/version.h> |
5 | #include <linux/netfilter_ipv4/ip_tables.h> | |
6 | #include <net/tcp.h> | |
7 | #include <net/udp.h> | |
0712d0fd | 8 | #include <asm/unaligned.h> |
44d6f47a JE |
9 | #include "xt_ipp2p.h" |
10 | #include "compat_xtables.h" | |
11 | ||
c237fe24 JE |
12 | //#define IPP2P_DEBUG_ARES |
13 | //#define IPP2P_DEBUG_SOUL | |
14 | //#define IPP2P_DEBUG_WINMX | |
15 | ||
74e7eb28 JE |
16 | #define get_u8(X, O) (*(const __u8 *)((X) + O)) |
17 | #define get_u16(X, O) get_unaligned((const __u16 *)((X) + O)) | |
18 | #define get_u32(X, O) get_unaligned((const __u32 *)((X) + O)) | |
44d6f47a JE |
19 | |
20 | MODULE_AUTHOR("Eicke Friedrich/Klaus Degner <ipp2p@ipp2p.org>"); | |
21 | MODULE_DESCRIPTION("An extension to iptables to identify P2P traffic."); | |
22 | MODULE_LICENSE("GPL"); | |
23 | ||
a6079ae9 JS |
24 | #if LINUX_VERSION_CODE < KERNEL_VERSION(5, 1, 0) |
25 | static inline unsigned int | |
26 | ip_transport_len(const struct sk_buff *skb) | |
27 | { | |
28 | return ntohs(ip_hdr(skb)->tot_len) - skb_network_header_len(skb); | |
29 | } | |
30 | static inline unsigned int | |
31 | ipv6_transport_len(const struct sk_buff *skb) | |
32 | { | |
33 | return ntohs(ipv6_hdr(skb)->payload_len) + sizeof(struct ipv6hdr) - | |
34 | skb_network_header_len(skb); | |
35 | } | |
36 | #endif | |
37 | ||
ad554f10 | 38 | struct ipp2p_result_printer { |
f144c2eb | 39 | const union nf_inet_addr *saddr, *daddr; |
ad554f10 | 40 | short sport, dport; |
f144c2eb | 41 | void (*print)(const union nf_inet_addr *, short, const union nf_inet_addr *, short, bool, unsigned int); |
ad554f10 JS |
42 | }; |
43 | ||
ee1c4c72 JS |
44 | static bool iscrlf(const unsigned char *str) |
45 | { | |
46 | return str[0] == '\r' && str[1] == '\n'; | |
47 | } | |
48 | ||
ad554f10 JS |
49 | static void |
50 | print_result(const struct ipp2p_result_printer *rp, bool result, | |
51 | unsigned int hlen) | |
52 | { | |
53 | rp->print(rp->saddr, rp->sport, | |
54 | rp->daddr, rp->dport, | |
55 | result, hlen); | |
56 | } | |
57 | ||
c237fe24 | 58 | /* Search for UDP eDonkey/eMule/Kad commands */ |
569643ac | 59 | static unsigned int |
30ba7eb4 JS |
60 | udp_search_edk(const unsigned char *t, const unsigned int packet_len, |
61 | const struct ipt_p2p_info *info) | |
44d6f47a | 62 | { |
17a03128 CB |
63 | if (packet_len < 4) |
64 | return 0; | |
65 | ||
44d6f47a | 66 | switch (t[0]) { |
c237fe24 | 67 | case 0xe3: |
30ddb4f8 | 68 | /* eDonkey */ |
c237fe24 JE |
69 | switch (t[1]) { |
70 | /* client -> server status request */ | |
71 | case 0x96: | |
c66d291e | 72 | if (packet_len == 6) |
c237fe24 | 73 | return IPP2P_EDK * 100 + 50; |
44d6f47a | 74 | break; |
c237fe24 JE |
75 | |
76 | /* server -> client status request */ | |
77 | case 0x97: | |
c66d291e | 78 | if (packet_len == 34) |
c237fe24 JE |
79 | return IPP2P_EDK * 100 + 51; |
80 | break; | |
81 | ||
82 | /* server description request */ | |
83 | /* e3 2a ff f0 .. | size == 6 */ | |
84 | case 0xa2: | |
c66d291e | 85 | if (packet_len == 6 && |
c237fe24 JE |
86 | get_u16(t, 2) == __constant_htons(0xfff0)) |
87 | return IPP2P_EDK * 100 + 52; | |
88 | break; | |
89 | ||
90 | /* server description response */ | |
91 | /* e3 a3 ff f0 .. | size > 40 && size < 200 */ | |
92 | /* | |
93 | case 0xa3: | |
94 | return IPP2P_EDK * 100 + 53; | |
95 | break; | |
96 | */ | |
97 | ||
98 | case 0x9a: | |
c66d291e | 99 | if (packet_len == 18) |
c237fe24 JE |
100 | return IPP2P_EDK * 100 + 54; |
101 | break; | |
102 | ||
103 | case 0x92: | |
c66d291e | 104 | if (packet_len == 10) |
c237fe24 | 105 | return IPP2P_EDK * 100 + 55; |
44d6f47a JE |
106 | break; |
107 | } | |
c237fe24 JE |
108 | break; |
109 | ||
110 | case 0xe4: | |
111 | switch (t[1]) { | |
c66d291e | 112 | /* e4 20 .. | size == 35 */ |
c237fe24 | 113 | case 0x20: |
c66d291e | 114 | if (packet_len == 35 && t[2] != 0x00 && t[34] != 0x00) |
c237fe24 JE |
115 | return IPP2P_EDK * 100 + 60; |
116 | break; | |
44d6f47a | 117 | |
c66d291e | 118 | /* e4 00 .. 00 | size == 27 ? */ |
c237fe24 | 119 | case 0x00: |
c66d291e | 120 | if (packet_len == 27 && t[26] == 0x00) |
c237fe24 | 121 | return IPP2P_EDK * 100 + 61; |
44d6f47a | 122 | break; |
c237fe24 | 123 | |
c66d291e | 124 | /* e4 10 .. 00 | size == 27 ? */ |
c237fe24 | 125 | case 0x10: |
c66d291e | 126 | if (packet_len == 27 && t[26] == 0x00) |
c237fe24 | 127 | return IPP2P_EDK * 100 + 62; |
44d6f47a | 128 | break; |
c237fe24 | 129 | |
c66d291e | 130 | /* e4 18 .. 00 | size == 27 ? */ |
c237fe24 | 131 | case 0x18: |
c66d291e | 132 | if (packet_len == 27 && t[26] == 0x00) |
c237fe24 | 133 | return IPP2P_EDK * 100 + 63; |
44d6f47a | 134 | break; |
c237fe24 | 135 | |
c66d291e | 136 | /* e4 52 .. | size = 36 */ |
c237fe24 | 137 | case 0x52: |
c66d291e | 138 | if (packet_len == 36) |
c237fe24 | 139 | return IPP2P_EDK * 100 + 64; |
44d6f47a | 140 | break; |
c237fe24 JE |
141 | |
142 | /* e4 58 .. | size == 6 */ | |
143 | case 0x58: | |
c66d291e | 144 | if (packet_len == 6) |
c237fe24 | 145 | return IPP2P_EDK * 100 + 65; |
44d6f47a | 146 | break; |
c237fe24 JE |
147 | |
148 | /* e4 59 .. | size == 2 */ | |
149 | case 0x59: | |
c66d291e | 150 | if (packet_len == 2) |
c237fe24 | 151 | return IPP2P_EDK * 100 + 66; |
44d6f47a | 152 | break; |
c237fe24 | 153 | |
c66d291e | 154 | /* e4 28 .. | packet_len == 49,69,94,119... */ |
c237fe24 | 155 | case 0x28: |
c66d291e | 156 | if ((packet_len - 44) % 25 == 0) |
c237fe24 | 157 | return IPP2P_EDK * 100 + 67; |
44d6f47a | 158 | break; |
c237fe24 JE |
159 | |
160 | /* e4 50 xx xx | size == 4 */ | |
161 | case 0x50: | |
c66d291e | 162 | if (packet_len == 4) |
c237fe24 JE |
163 | return IPP2P_EDK * 100 + 68; |
164 | break; | |
165 | ||
166 | /* e4 40 xx xx | size == 48 */ | |
167 | case 0x40: | |
c66d291e | 168 | if (packet_len == 48) |
c237fe24 | 169 | return IPP2P_EDK * 100 + 69; |
44d6f47a | 170 | break; |
44d6f47a | 171 | } |
c237fe24 | 172 | break; |
44d6f47a | 173 | } |
c237fe24 JE |
174 | return 0; |
175 | } | |
176 | ||
177 | /* Search for UDP Gnutella commands */ | |
569643ac | 178 | static unsigned int |
30ba7eb4 JS |
179 | udp_search_gnu(const unsigned char *t, const unsigned int packet_len, |
180 | const struct ipt_p2p_info *info) | |
c237fe24 | 181 | { |
17a03128 | 182 | if (packet_len >= 3 && memcmp(t, "GND", 3) == 0) |
c237fe24 | 183 | return IPP2P_GNU * 100 + 51; |
17a03128 | 184 | if (packet_len >= 9 && memcmp(t, "GNUTELLA ", 9) == 0) |
c237fe24 JE |
185 | return IPP2P_GNU * 100 + 52; |
186 | return 0; | |
187 | } | |
188 | ||
189 | /* Search for UDP KaZaA commands */ | |
569643ac | 190 | static unsigned int |
30ba7eb4 JS |
191 | udp_search_kazaa(const unsigned char *t, const unsigned int packet_len, |
192 | const struct ipt_p2p_info *info) | |
c237fe24 | 193 | { |
17a03128 CB |
194 | if (packet_len < 6) |
195 | return 0; | |
196 | if (memcmp(t + packet_len - 6, "KaZaA\x00", 6) == 0) | |
197 | return IPP2P_KAZAA * 100 + 50; | |
c237fe24 JE |
198 | return 0; |
199 | } | |
200 | ||
201 | /* Search for UDP DirectConnect commands */ | |
30ba7eb4 JS |
202 | static unsigned int |
203 | udp_search_directconnect(const unsigned char *t, const unsigned int packet_len, | |
204 | const struct ipt_p2p_info *info) | |
c237fe24 | 205 | { |
17a03128 CB |
206 | if (packet_len < 5) |
207 | return 0; | |
dc05fc62 JS |
208 | if (t[0] != 0x24) |
209 | return 0; | |
210 | if (t[packet_len-1] != 0x7c) | |
211 | return 0; | |
212 | if (memcmp(&t[1], "SR ", 3) == 0) | |
213 | return IPP2P_DC * 100 + 60; | |
214 | if (packet_len >= 7 && memcmp(&t[1], "Ping ", 5) == 0) | |
215 | return IPP2P_DC * 100 + 61; | |
c237fe24 JE |
216 | return 0; |
217 | } | |
218 | ||
219 | /* Search for UDP BitTorrent commands */ | |
569643ac | 220 | static unsigned int |
30ba7eb4 JS |
221 | udp_search_bit(const unsigned char *haystack, const unsigned int packet_len, |
222 | const struct ipt_p2p_info *info) | |
c237fe24 JE |
223 | { |
224 | switch (packet_len) { | |
c66d291e | 225 | case 16: |
c237fe24 | 226 | /* ^ 00 00 04 17 27 10 19 80 */ |
2c5d0956 JS |
227 | if (get_u32(haystack, 0) == __constant_htonl(0x00000417) && |
228 | get_u32(haystack, 4) == __constant_htonl(0x27101980)) | |
c237fe24 JE |
229 | return IPP2P_BIT * 100 + 50; |
230 | break; | |
c66d291e CB |
231 | case 36: |
232 | if (get_u32(haystack, 8) == __constant_htonl(0x00000400) && | |
233 | get_u32(haystack, 28) == __constant_htonl(0x00000104)) | |
c237fe24 | 234 | return IPP2P_BIT * 100 + 51; |
c66d291e | 235 | if (get_u32(haystack, 8) == __constant_htonl(0x00000400)) |
c237fe24 JE |
236 | return IPP2P_BIT * 100 + 61; |
237 | break; | |
c66d291e CB |
238 | case 57: |
239 | if (get_u32(haystack, 8) == __constant_htonl(0x00000404) && | |
240 | get_u32(haystack, 28) == __constant_htonl(0x00000104)) | |
c237fe24 | 241 | return IPP2P_BIT * 100 + 52; |
c66d291e | 242 | if (get_u32(haystack, 8) == __constant_htonl(0x00000404)) |
c237fe24 JE |
243 | return IPP2P_BIT * 100 + 62; |
244 | break; | |
c66d291e CB |
245 | case 59: |
246 | if (get_u32(haystack, 8) == __constant_htonl(0x00000406) && | |
247 | get_u32(haystack, 28) == __constant_htonl(0x00000104)) | |
c237fe24 | 248 | return (IPP2P_BIT * 100 + 53); |
c66d291e | 249 | if (get_u32(haystack, 8) == __constant_htonl(0x00000406)) |
c237fe24 JE |
250 | return (IPP2P_BIT * 100 + 63); |
251 | break; | |
c66d291e CB |
252 | case 203: |
253 | if (get_u32(haystack, 0) == __constant_htonl(0x00000405)) | |
c237fe24 JE |
254 | return IPP2P_BIT * 100 + 54; |
255 | break; | |
c66d291e CB |
256 | case 21: |
257 | if (get_u32(haystack, 0) == __constant_htonl(0x00000401)) | |
c237fe24 JE |
258 | return IPP2P_BIT * 100 + 55; |
259 | break; | |
c66d291e CB |
260 | case 44: |
261 | if (get_u32(haystack, 0) == __constant_htonl(0x00000827) && | |
262 | get_u32(haystack, 4) == __constant_htonl(0x37502950)) | |
c237fe24 JE |
263 | return IPP2P_BIT * 100 + 80; |
264 | break; | |
265 | default: | |
266 | /* this packet does not have a constant size */ | |
c66d291e CB |
267 | if (packet_len >= 32 && |
268 | get_u32(haystack, 8) == __constant_htonl(0x00000402) && | |
269 | get_u32(haystack, 28) == __constant_htonl(0x00000104)) | |
c237fe24 JE |
270 | return IPP2P_BIT * 100 + 56; |
271 | break; | |
272 | } | |
273 | ||
274 | /* some extra-bitcomet rules: "d1:" [a|r] "d2:id20:" */ | |
dc05fc62 JS |
275 | if (packet_len > 22 && |
276 | get_u8(haystack, 0) == 'd' && | |
277 | get_u8(haystack, 1) == '1' && | |
278 | get_u8(haystack, 2) == ':' && | |
279 | (get_u8(haystack, 3) == 'a' || | |
280 | get_u8(haystack, 3) == 'r') && | |
281 | memcmp(haystack + 4, "d2:id20:", 8) == 0) | |
282 | return IPP2P_BIT * 100 + 57; | |
c237fe24 | 283 | |
44d6f47a JE |
284 | #if 0 |
285 | /* bitlord rules */ | |
c66d291e | 286 | /* packetlen must be bigger than 32 */ |
44d6f47a | 287 | /* first 4 bytes are zero */ |
c66d291e | 288 | if (packet_len > 32 && get_u32(haystack, 0) == 0x00000000) { |
30ddb4f8 | 289 | /* first rule: 00 00 00 00 01 00 00 xx xx xx xx 00 00 00 00 */ |
c66d291e CB |
290 | if (get_u32(haystack, 4) == 0x00000000 && |
291 | get_u32(haystack, 8) == 0x00010000 && | |
292 | get_u32(haystack, 16) == 0x00000000) | |
c237fe24 JE |
293 | return IPP2P_BIT * 100 + 71; |
294 | ||
30ddb4f8 | 295 | /* 00 01 00 00 0d 00 00 xx xx xx xx 00 00 00 00 */ |
c66d291e CB |
296 | if (get_u32(haystack, 4) == 0x00000001 && |
297 | get_u32(haystack, 8) == 0x000d0000 && | |
298 | get_u32(haystack, 16) == 0x00000000) | |
c237fe24 | 299 | return IPP2P_BIT * 100 + 71; |
44d6f47a JE |
300 | } |
301 | #endif | |
302 | ||
c237fe24 JE |
303 | return 0; |
304 | } | |
44d6f47a | 305 | |
c237fe24 | 306 | /* Search for Ares commands */ |
569643ac | 307 | static unsigned int |
30ba7eb4 JS |
308 | search_ares(const unsigned char *payload, const unsigned int plen, |
309 | const struct ipt_p2p_info *info) | |
44d6f47a | 310 | { |
17a03128 CB |
311 | if (plen < 3) |
312 | return 0; | |
44d6f47a | 313 | /* all ares packets start with */ |
c237fe24 JE |
314 | if (payload[1] == 0 && plen - payload[0] == 3) { |
315 | switch (payload[2]) { | |
316 | case 0x5a: | |
317 | /* ares connect */ | |
318 | if (plen == 6 && payload[5] == 0x05) | |
319 | return IPP2P_ARES * 100 + 1; | |
320 | break; | |
321 | case 0x09: | |
322 | /* | |
323 | * ares search, min 3 chars --> 14 bytes | |
324 | * lets define a search can be up to 30 chars | |
325 | * --> max 34 bytes | |
326 | */ | |
327 | if (plen >= 14 && plen <= 34) | |
328 | return IPP2P_ARES * 100 + 1; | |
329 | break; | |
44d6f47a | 330 | #ifdef IPP2P_DEBUG_ARES |
c237fe24 JE |
331 | default: |
332 | printk(KERN_DEBUG "Unknown Ares command %x " | |
333 | "recognized, len: %u\n", | |
334 | (unsigned int)payload[2], plen); | |
335 | #endif | |
44d6f47a JE |
336 | } |
337 | } | |
338 | ||
c237fe24 | 339 | #if 0 |
44d6f47a JE |
340 | /* found connect packet: 03 00 5a 04 03 05 */ |
341 | /* new version ares 1.8: 03 00 5a xx xx 05 */ | |
c237fe24 JE |
342 | if (plen == 6) |
343 | /* possible connect command */ | |
344 | if (payload[0] == 0x03 && payload[1] == 0x00 && | |
345 | payload[2] == 0x5a && payload[5] == 0x05) | |
346 | return IPP2P_ARES * 100 + 1; | |
347 | ||
348 | if (plen == 60) | |
30ddb4f8 | 349 | /* possible download command */ |
c237fe24 JE |
350 | if (payload[59] == 0x0a && payload[58] == 0x0a) |
351 | if (memcmp(t, "PUSH SHA1:", 10) == 0) | |
352 | /* found download command */ | |
353 | return IPP2P_ARES * 100 + 2; | |
44d6f47a JE |
354 | #endif |
355 | ||
c237fe24 JE |
356 | return 0; |
357 | } | |
44d6f47a | 358 | |
c237fe24 | 359 | /* Search for SoulSeek commands */ |
569643ac | 360 | static unsigned int |
30ba7eb4 JS |
361 | search_soul(const unsigned char *payload, const unsigned int plen, |
362 | const struct ipt_p2p_info *info) | |
44d6f47a | 363 | { |
17a03128 CB |
364 | if (plen < 8) |
365 | return 0; | |
c237fe24 JE |
366 | /* match: xx xx xx xx | xx = sizeof(payload) - 4 */ |
367 | if (get_u32(payload, 0) == plen - 4) { | |
569643ac JE |
368 | const uint32_t m = get_u32(payload, 4); |
369 | ||
c237fe24 JE |
370 | /* match 00 yy yy 00, yy can be everything */ |
371 | if (get_u8(payload, 4) == 0x00 && get_u8(payload, 7) == 0x00) { | |
44d6f47a | 372 | #ifdef IPP2P_DEBUG_SOUL |
c237fe24 JE |
373 | printk(KERN_DEBUG "0: Soulseek command 0x%x " |
374 | "recognized\n", get_u32(payload, 4)); | |
375 | #endif | |
376 | return IPP2P_SOUL * 100 + 1; | |
377 | } | |
378 | ||
379 | /* next match: 01 yy 00 00 | yy can be everything */ | |
380 | if (get_u8(payload, 4) == 0x01 && get_u16(payload, 6) == 0x0000) { | |
44d6f47a | 381 | #ifdef IPP2P_DEBUG_SOUL |
c237fe24 JE |
382 | printk(KERN_DEBUG "1: Soulseek command 0x%x " |
383 | "recognized\n", get_u16(payload, 4)); | |
384 | #endif | |
385 | return IPP2P_SOUL * 100 + 2; | |
386 | } | |
387 | ||
388 | /* 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 */ | |
389 | /* try to do this in an intelligent way */ | |
390 | /* get all small commandos */ | |
391 | switch (m) { | |
44d6f47a JE |
392 | case 7: |
393 | case 9: | |
394 | case 22: | |
395 | case 23: | |
396 | case 26: | |
397 | case 28: | |
398 | case 50: | |
399 | case 51: | |
400 | case 60: | |
401 | case 91: | |
402 | case 92: | |
403 | case 1001: | |
404 | #ifdef IPP2P_DEBUG_SOUL | |
c237fe24 JE |
405 | printk(KERN_DEBUG "2: Soulseek command 0x%x " |
406 | "recognized\n", get_u16(payload, 4)); | |
407 | #endif | |
408 | return IPP2P_SOUL * 100 + 3; | |
409 | } | |
410 | ||
411 | if (m > 0 && m < 6) { | |
44d6f47a | 412 | #ifdef IPP2P_DEBUG_SOUL |
c237fe24 JE |
413 | printk(KERN_DEBUG "3: Soulseek command 0x%x " |
414 | "recognized\n", get_u16(payload, 4)); | |
415 | #endif | |
416 | return IPP2P_SOUL * 100 + 4; | |
417 | } | |
418 | ||
419 | if (m > 12 && m < 19) { | |
44d6f47a | 420 | #ifdef IPP2P_DEBUG_SOUL |
c237fe24 JE |
421 | printk(KERN_DEBUG "4: Soulseek command 0x%x " |
422 | "recognized\n", get_u16(payload, 4)); | |
423 | #endif | |
424 | return IPP2P_SOUL * 100 + 5; | |
425 | } | |
44d6f47a | 426 | |
c237fe24 | 427 | if (m > 34 && m < 38) { |
44d6f47a | 428 | #ifdef IPP2P_DEBUG_SOUL |
c237fe24 JE |
429 | printk(KERN_DEBUG "5: Soulseek command 0x%x " |
430 | "recognized\n", get_u16(payload, 4)); | |
431 | #endif | |
432 | return IPP2P_SOUL * 100 + 6; | |
433 | } | |
44d6f47a | 434 | |
c237fe24 | 435 | if (m > 39 && m < 47) { |
44d6f47a | 436 | #ifdef IPP2P_DEBUG_SOUL |
c237fe24 JE |
437 | printk(KERN_DEBUG "6: Soulseek command 0x%x " |
438 | "recognized\n", get_u16(payload, 4)); | |
439 | #endif | |
440 | return IPP2P_SOUL * 100 + 7; | |
441 | } | |
44d6f47a | 442 | |
c237fe24 | 443 | if (m > 61 && m < 70) { |
44d6f47a | 444 | #ifdef IPP2P_DEBUG_SOUL |
c237fe24 JE |
445 | printk(KERN_DEBUG "7: Soulseek command 0x%x " |
446 | "recognized\n", get_u16(payload, 4)); | |
447 | #endif | |
448 | return IPP2P_SOUL * 100 + 8; | |
449 | } | |
44d6f47a JE |
450 | |
451 | #ifdef IPP2P_DEBUG_SOUL | |
c237fe24 JE |
452 | printk(KERN_DEBUG "unknown SOULSEEK command: 0x%x, first " |
453 | "16 bit: 0x%x, first 8 bit: 0x%x ,soulseek ???\n", | |
454 | get_u32(payload, 4), get_u16(payload, 4) >> 16, | |
455 | get_u8(payload, 4) >> 24); | |
456 | #endif | |
457 | } | |
458 | ||
44d6f47a | 459 | /* match 14 00 00 00 01 yy 00 00 00 STRING(YY) 01 00 00 00 00 46|50 00 00 00 00 */ |
30ddb4f8 | 460 | /* without size at the beginning! */ |
c237fe24 | 461 | if (get_u32(payload, 0) == 0x14 && get_u8(payload, 4) == 0x01) { |
569643ac | 462 | uint32_t y = get_u32(payload, 5); |
dc05fc62 | 463 | const unsigned char *w; |
569643ac | 464 | |
44d6f47a | 465 | /* we need 19 chars + string */ |
dc05fc62 JS |
466 | if (plen < y + 19) |
467 | return 0; | |
468 | w = payload + 9 + y; | |
469 | if (get_u32(w, 0) == 0x01 && | |
470 | (get_u16(w, 4) == 0x4600 || | |
471 | get_u16(w, 4) == 0x5000) && | |
472 | get_u32(w, 6) == 0x00) { | |
44d6f47a | 473 | #ifdef IPP2P_DEBUG_SOUL |
dc05fc62 | 474 | printk(KERN_DEBUG "Soulseek special client command recognized\n"); |
c237fe24 | 475 | #endif |
dc05fc62 | 476 | return IPP2P_SOUL * 100 + 9; |
44d6f47a JE |
477 | } |
478 | } | |
c237fe24 | 479 | return 0; |
44d6f47a JE |
480 | } |
481 | ||
c237fe24 | 482 | /* Search for WinMX commands */ |
569643ac | 483 | static unsigned int |
30ba7eb4 JS |
484 | search_winmx(const unsigned char *payload, const unsigned int plen, |
485 | const struct ipt_p2p_info *info) | |
44d6f47a | 486 | { |
30ba7eb4 JS |
487 | uint16_t start; |
488 | ||
c237fe24 JE |
489 | if (plen == 4 && memcmp(payload, "SEND", 4) == 0) |
490 | return IPP2P_WINMX * 100 + 1; | |
491 | if (plen == 3 && memcmp(payload, "GET", 3) == 0) | |
492 | return IPP2P_WINMX * 100 + 2; | |
493 | /* | |
494 | if (packet_len < head_len + 10) | |
495 | return 0; | |
496 | */ | |
497 | if (plen < 10) | |
498 | return 0; | |
499 | ||
30ba7eb4 JS |
500 | if (memcmp(payload, "SEND", 4) == 0) |
501 | start = 4; | |
502 | else if (memcmp(payload, "GET", 3) == 0) | |
503 | start = 3; | |
504 | else | |
505 | start = 0; | |
506 | ||
507 | if (start) { | |
569643ac | 508 | uint8_t count = 0; |
c237fe24 | 509 | |
30ba7eb4 JS |
510 | do { |
511 | struct ts_state state; | |
512 | unsigned int pos; | |
513 | ||
514 | pos = textsearch_find_continuous(info->ts_conf_winmx, | |
515 | &state, | |
516 | &payload[start], | |
517 | plen - start); | |
518 | if (pos == UINT_MAX) | |
519 | break; | |
520 | ||
521 | count++; | |
522 | if (count >= 2) | |
523 | return IPP2P_WINMX * 100 + 3; | |
524 | ||
525 | start = pos + 2; | |
526 | } while (start < plen); | |
c237fe24 JE |
527 | } |
528 | ||
529 | if (plen == 149 && payload[0] == '8') { | |
44d6f47a | 530 | #ifdef IPP2P_DEBUG_WINMX |
c237fe24 | 531 | printk(KERN_INFO "maybe WinMX\n"); |
44d6f47a | 532 | #endif |
c237fe24 JE |
533 | if (get_u32(payload, 17) == 0 && get_u32(payload, 21) == 0 && |
534 | get_u32(payload, 25) == 0 && | |
535 | // get_u32(payload, 33) == __constant_htonl(0x71182b1a) && | |
536 | // get_u32(payload, 37) == __constant_htonl(0x05050000) && | |
537 | // get_u32(payload, 133) == __constant_htonl(0x31097edf) && | |
538 | // get_u32(payload, 145) == __constant_htonl(0xdcb8f792)) | |
539 | get_u16(payload, 39) == 0 && | |
540 | get_u16(payload, 135) == __constant_htons(0x7edf) && | |
541 | get_u16(payload,147) == __constant_htons(0xf792)) | |
542 | { | |
44d6f47a | 543 | #ifdef IPP2P_DEBUG_WINMX |
c237fe24 | 544 | printk(KERN_INFO "got WinMX\n"); |
44d6f47a | 545 | #endif |
c237fe24 JE |
546 | return IPP2P_WINMX * 100 + 4; |
547 | } | |
548 | } | |
549 | return 0; | |
550 | } | |
551 | ||
552 | /* Search for appleJuice commands */ | |
569643ac | 553 | static unsigned int |
30ba7eb4 JS |
554 | search_apple(const unsigned char *payload, const unsigned int plen, |
555 | const struct ipt_p2p_info *info) | |
44d6f47a | 556 | { |
dc05fc62 JS |
557 | if (plen < 8) |
558 | return 0; | |
559 | if (memcmp(payload, "ajprot\r\n", 8) == 0) | |
c237fe24 | 560 | return IPP2P_APPLE * 100; |
c237fe24 | 561 | return 0; |
44d6f47a JE |
562 | } |
563 | ||
c237fe24 | 564 | /* Search for BitTorrent commands */ |
569643ac | 565 | static unsigned int |
30ba7eb4 JS |
566 | search_bittorrent(const unsigned char *payload, const unsigned int plen, |
567 | const struct ipt_p2p_info *info) | |
44d6f47a | 568 | { |
30ba7eb4 JS |
569 | struct ts_state state; |
570 | unsigned int pos; | |
571 | ||
dc05fc62 JS |
572 | /* |
573 | * bitcomet encrypts the first packet, so we have to detect another one | |
574 | * later in the flow. | |
575 | */ | |
576 | if (plen == 17 && | |
577 | get_u32(payload, 0) == __constant_htonl(0x0d) && | |
578 | payload[4] == 0x06 && | |
579 | get_u32(payload,13) == __constant_htonl(0x4000)) | |
580 | return IPP2P_BIT * 100 + 3; | |
581 | if (plen <= 20) | |
582 | return 0; | |
c237fe24 | 583 | |
dc05fc62 JS |
584 | /* test for match 0x13+"BitTorrent protocol" */ |
585 | if (payload[0] == 0x13) | |
586 | if (memcmp(payload + 1, "BitTorrent protocol", 19) == 0) | |
587 | return IPP2P_BIT * 100; | |
588 | /* | |
589 | * Any tracker command starts with GET / then *may be* some file | |
590 | * on web server (e.g. announce.php or dupa.pl or whatever.cgi | |
591 | * or NOTHING for tracker on root dir) but *must have* one (or | |
592 | * more) of strings listed below (true for scrape and announce) | |
593 | */ | |
594 | if (memcmp(payload, "GET /", 5) != 0) | |
595 | return 0; | |
30ba7eb4 JS |
596 | |
597 | pos = textsearch_find_continuous(info->ts_conf_bt_info_hash, | |
598 | &state, &payload[5], plen - 5); | |
599 | if (pos != UINT_MAX) | |
dc05fc62 | 600 | return IPP2P_BIT * 100 + 1; |
30ba7eb4 JS |
601 | |
602 | pos = textsearch_find_continuous(info->ts_conf_bt_peer_id, | |
603 | &state, &payload[5], plen - 5); | |
604 | if (pos != UINT_MAX) | |
dc05fc62 | 605 | return IPP2P_BIT * 100 + 2; |
30ba7eb4 JS |
606 | |
607 | pos = textsearch_find_continuous(info->ts_conf_bt_passkey, | |
608 | &state, &payload[5], plen - 5); | |
609 | if (pos != UINT_MAX) | |
dc05fc62 | 610 | return IPP2P_BIT * 100 + 4; |
c237fe24 | 611 | return 0; |
44d6f47a JE |
612 | } |
613 | ||
c237fe24 | 614 | /* check for Kazaa get command */ |
569643ac | 615 | static unsigned int |
30ba7eb4 JS |
616 | search_kazaa(const unsigned char *payload, const unsigned int plen, |
617 | const struct ipt_p2p_info *info) | |
44d6f47a | 618 | { |
17a03128 CB |
619 | if (plen < 13) |
620 | return 0; | |
ee1c4c72 | 621 | if (iscrlf(&payload[plen-2]) && |
c237fe24 JE |
622 | memcmp(payload, "GET /.hash=", 11) == 0) |
623 | return IPP2P_DATA_KAZAA * 100; | |
44d6f47a | 624 | |
c237fe24 | 625 | return 0; |
44d6f47a JE |
626 | } |
627 | ||
30ddb4f8 | 628 | /* check for Gnutella get command */ |
569643ac | 629 | static unsigned int |
30ba7eb4 JS |
630 | search_gnu(const unsigned char *payload, const unsigned int plen, |
631 | const struct ipt_p2p_info *info) | |
44d6f47a | 632 | { |
17a03128 CB |
633 | if (plen < 11) |
634 | return 0; | |
dc05fc62 JS |
635 | if (!iscrlf(&payload[plen-2])) |
636 | return 0; | |
637 | if (memcmp(payload, "GET /get/", 9) == 0) | |
638 | return IPP2P_DATA_GNU * 100 + 1; | |
639 | if (plen >= 15 && memcmp(payload, "GET /uri-res/", 13) == 0) | |
640 | return IPP2P_DATA_GNU * 100 + 2; | |
c237fe24 | 641 | return 0; |
44d6f47a JE |
642 | } |
643 | ||
30ddb4f8 | 644 | /* check for Gnutella get commands and other typical data */ |
569643ac | 645 | static unsigned int |
30ba7eb4 JS |
646 | search_all_gnu(const unsigned char *payload, const unsigned int plen, |
647 | const struct ipt_p2p_info *info) | |
44d6f47a | 648 | { |
30ba7eb4 JS |
649 | struct ts_state state; |
650 | unsigned int c, pos; | |
dc05fc62 | 651 | |
17a03128 CB |
652 | if (plen < 11) |
653 | return 0; | |
dc05fc62 JS |
654 | if (!iscrlf(&payload[plen-2])) |
655 | return 0; | |
656 | if (plen >= 19 && memcmp(payload, "GNUTELLA CONNECT/", 17) == 0) | |
657 | return IPP2P_GNU * 100 + 1; | |
658 | if (memcmp(payload, "GNUTELLA/", 9) == 0) | |
659 | return IPP2P_GNU * 100 + 2; | |
660 | if (plen < 22) | |
661 | return 0; | |
30ba7eb4 JS |
662 | if (memcmp(payload, "GET /get/", 9) == 0) |
663 | c = 9; | |
664 | else if (memcmp(payload, "GET /uri-res/", 13) == 0) | |
665 | c = 13; | |
666 | else | |
dc05fc62 | 667 | return 0; |
569643ac | 668 | |
30ba7eb4 JS |
669 | pos = textsearch_find_continuous(info->ts_conf_gnu_x_gnutella, |
670 | &state, &payload[c], plen - c); | |
671 | if (pos != UINT_MAX) | |
672 | return IPP2P_GNU * 100 + 3; | |
673 | ||
674 | pos = textsearch_find_continuous(info->ts_conf_gnu_x_queue, | |
675 | &state, &payload[c], plen - c); | |
676 | if (pos != UINT_MAX) | |
677 | return IPP2P_GNU * 100 + 3; | |
c237fe24 | 678 | return 0; |
44d6f47a JE |
679 | } |
680 | ||
c237fe24 | 681 | /* check for KaZaA download commands and other typical data */ |
d01a5f3d | 682 | /* plen is guaranteed to be >= 5 (see @matchlist) */ |
569643ac | 683 | static unsigned int |
30ba7eb4 JS |
684 | search_all_kazaa(const unsigned char *payload, const unsigned int plen, |
685 | const struct ipt_p2p_info *info) | |
44d6f47a | 686 | { |
30ba7eb4 JS |
687 | struct ts_state state; |
688 | unsigned int pos; | |
d01a5f3d | 689 | |
17a03128 | 690 | if (plen < 7) |
879e964f JE |
691 | /* too short for anything we test for - early bailout */ |
692 | return 0; | |
ee1c4c72 | 693 | if (!iscrlf(&payload[plen-2])) |
22db3bcb | 694 | return 0; |
c237fe24 | 695 | |
22db3bcb JE |
696 | if (memcmp(payload, "GIVE ", 5) == 0) |
697 | return IPP2P_KAZAA * 100 + 1; | |
698 | ||
d01a5f3d JE |
699 | if (memcmp(payload, "GET /", 5) != 0) |
700 | return 0; | |
701 | ||
4cdfd496 JE |
702 | if (plen < 18) |
703 | /* The next tests would not succeed anyhow. */ | |
704 | return 0; | |
705 | ||
30ba7eb4 JS |
706 | pos = textsearch_find_continuous(info->ts_conf_kz_x_kazaa_username, |
707 | &state, &payload[5], plen - 5); | |
708 | if (pos != UINT_MAX) | |
709 | return IPP2P_KAZAA * 100 + 2; | |
710 | ||
711 | pos = textsearch_find_continuous(info->ts_conf_kz_user_agent, | |
712 | &state, &payload[5], plen - 5); | |
713 | if (pos != UINT_MAX) | |
714 | return IPP2P_KAZAA * 100 + 2; | |
22db3bcb | 715 | |
c237fe24 | 716 | return 0; |
44d6f47a JE |
717 | } |
718 | ||
30ddb4f8 | 719 | /* fast check for eDonkey file segment transfer command */ |
569643ac | 720 | static unsigned int |
30ba7eb4 JS |
721 | search_edk(const unsigned char *payload, const unsigned int plen, |
722 | const struct ipt_p2p_info *info) | |
44d6f47a | 723 | { |
17a03128 CB |
724 | if (plen < 6) |
725 | return 0; | |
dc05fc62 | 726 | if (payload[0] != 0xe3) |
c237fe24 | 727 | return 0; |
dc05fc62 JS |
728 | if (payload[5] == 0x47) |
729 | return IPP2P_DATA_EDK * 100; | |
730 | return 0; | |
44d6f47a JE |
731 | } |
732 | ||
30ddb4f8 | 733 | /* intensive but slower search for some eDonkey packets including size check */ |
569643ac | 734 | static unsigned int |
30ba7eb4 JS |
735 | search_all_edk(const unsigned char *payload, const unsigned int plen, |
736 | const struct ipt_p2p_info *info) | |
44d6f47a | 737 | { |
dc05fc62 JS |
738 | unsigned int cmd; |
739 | ||
17a03128 CB |
740 | if (plen < 6) |
741 | return 0; | |
dc05fc62 | 742 | if (payload[0] != 0xe3) |
c237fe24 | 743 | return 0; |
c237fe24 | 744 | |
dc05fc62 JS |
745 | cmd = get_u16(payload, 1); |
746 | if (cmd == plen - 5) { | |
747 | switch (payload[5]) { | |
748 | case 0x01: | |
749 | /* Client: hello or Server:hello */ | |
c237fe24 | 750 | return IPP2P_EDK * 100 + 1; |
dc05fc62 JS |
751 | case 0x4c: |
752 | /* Client: Hello-Answer */ | |
753 | return IPP2P_EDK * 100 + 9; | |
c237fe24 | 754 | } |
44d6f47a | 755 | } |
dc05fc62 | 756 | return 0; |
44d6f47a JE |
757 | } |
758 | ||
c237fe24 | 759 | /* fast check for Direct Connect send command */ |
569643ac | 760 | static unsigned int |
30ba7eb4 JS |
761 | search_dc(const unsigned char *payload, const unsigned int plen, |
762 | const struct ipt_p2p_info *info) | |
44d6f47a | 763 | { |
17a03128 CB |
764 | if (plen < 6) |
765 | return 0; | |
dc05fc62 | 766 | if (payload[0] != 0x24) |
c237fe24 | 767 | return 0; |
dc05fc62 JS |
768 | if (memcmp(&payload[1], "Send|", 5) == 0) |
769 | return IPP2P_DATA_DC * 100; | |
770 | return 0; | |
44d6f47a JE |
771 | } |
772 | ||
c237fe24 | 773 | /* intensive but slower check for all direct connect packets */ |
569643ac | 774 | static unsigned int |
30ba7eb4 JS |
775 | search_all_dc(const unsigned char *payload, const unsigned int plen, |
776 | const struct ipt_p2p_info *info) | |
44d6f47a | 777 | { |
dc05fc62 JS |
778 | const unsigned char *t; |
779 | ||
17a03128 CB |
780 | if (plen < 7) |
781 | return 0; | |
dc05fc62 JS |
782 | if (payload[0] != 0x24) |
783 | return 0; | |
784 | if (payload[plen-1] != 0x7c) | |
785 | return 0; | |
786 | t = &payload[1]; | |
787 | /* Client-Hub-Protocol */ | |
788 | if (memcmp(t, "Lock ", 5) == 0) | |
789 | return IPP2P_DC * 100 + 1; | |
790 | /* | |
791 | * Client-Client-Protocol, some are already recognized by client-hub | |
792 | * (like lock) | |
793 | */ | |
794 | if (plen >= 9 && memcmp(t, "MyNick ", 7) == 0) | |
795 | return IPP2P_DC * 100 + 38; | |
c237fe24 | 796 | return 0; |
44d6f47a JE |
797 | } |
798 | ||
c237fe24 | 799 | /* check for mute */ |
569643ac | 800 | static unsigned int |
30ba7eb4 JS |
801 | search_mute(const unsigned char *payload, const unsigned int plen, |
802 | const struct ipt_p2p_info *info) | |
44d6f47a | 803 | { |
c237fe24 JE |
804 | if (plen == 209 || plen == 345 || plen == 473 || plen == 609 || |
805 | plen == 1121) { | |
806 | //printk(KERN_DEBUG "size hit: %u", size); | |
807 | if (memcmp(payload,"PublicKey: ", 11) == 0) { | |
808 | return IPP2P_MUTE * 100 + 0; | |
809 | /* | |
810 | if (memcmp(t + size - 14, "\x0aEndPublicKey\x0a", 14) == 0) | |
811 | printk(KERN_DEBUG "end pubic key hit: %u", size); | |
812 | */ | |
44d6f47a JE |
813 | } |
814 | } | |
815 | return 0; | |
816 | } | |
817 | ||
44d6f47a | 818 | /* check for xdcc */ |
569643ac | 819 | static unsigned int |
30ba7eb4 JS |
820 | search_xdcc(const unsigned char *payload, const unsigned int plen, |
821 | const struct ipt_p2p_info *info) | |
44d6f47a | 822 | { |
30ba7eb4 JS |
823 | struct ts_state state; |
824 | unsigned int pos; | |
c237fe24 | 825 | |
dc05fc62 JS |
826 | /* search in small packets only */ |
827 | if (plen <= 20 || plen >= 200) | |
828 | return 0; | |
829 | if (memcmp(payload, "PRIVMSG ", 8) != 0 || !iscrlf(&payload[plen - 2])) | |
830 | return 0; | |
831 | /* | |
832 | * It seems to be an IRC private message, check for xdcc command | |
833 | */ | |
30ba7eb4 JS |
834 | pos = textsearch_find_continuous(info->ts_conf_xdcc, |
835 | &state, &payload[8], plen - 8); | |
836 | if (pos != UINT_MAX) | |
837 | return IPP2P_XDCC * 100 + 0; | |
838 | ||
44d6f47a JE |
839 | return 0; |
840 | } | |
841 | ||
842 | /* search for waste */ | |
569643ac | 843 | static unsigned int |
30ba7eb4 JS |
844 | search_waste(const unsigned char *payload, const unsigned int plen, |
845 | const struct ipt_p2p_info *info) | |
44d6f47a | 846 | { |
38a85247 | 847 | if (plen >= 9 && memcmp(payload, "GET.sha1:", 9) == 0) |
c237fe24 | 848 | return IPP2P_WASTE * 100 + 0; |
44d6f47a JE |
849 | |
850 | return 0; | |
851 | } | |
852 | ||
569643ac JE |
853 | static const struct { |
854 | unsigned int command; | |
569643ac | 855 | unsigned int packet_len; |
30ba7eb4 JS |
856 | unsigned int (*function_name)(const unsigned char *, const unsigned int, |
857 | const struct ipt_p2p_info *); | |
44d6f47a | 858 | } matchlist[] = { |
a1d307e3 | 859 | {IPP2P_EDK, 20, search_all_edk}, |
01df89eb JE |
860 | {IPP2P_DATA_KAZAA, 200, search_kazaa}, /* exp */ |
861 | {IPP2P_DATA_EDK, 60, search_edk}, /* exp */ | |
862 | {IPP2P_DATA_DC, 26, search_dc}, /* exp */ | |
a1d307e3 | 863 | {IPP2P_DC, 5, search_all_dc}, |
01df89eb | 864 | {IPP2P_DATA_GNU, 40, search_gnu}, /* exp */ |
a1d307e3 JE |
865 | {IPP2P_GNU, 5, search_all_gnu}, |
866 | {IPP2P_KAZAA, 5, search_all_kazaa}, | |
867 | {IPP2P_BIT, 20, search_bittorrent}, | |
868 | {IPP2P_APPLE, 5, search_apple}, | |
869 | {IPP2P_SOUL, 5, search_soul}, | |
870 | {IPP2P_WINMX, 2, search_winmx}, | |
871 | {IPP2P_ARES, 5, search_ares}, | |
872 | {IPP2P_MUTE, 200, search_mute}, | |
873 | {IPP2P_WASTE, 5, search_waste}, | |
874 | {IPP2P_XDCC, 5, search_xdcc}, | |
c237fe24 | 875 | {0}, |
44d6f47a JE |
876 | }; |
877 | ||
569643ac JE |
878 | static const struct { |
879 | unsigned int command; | |
569643ac | 880 | unsigned int packet_len; |
30ba7eb4 JS |
881 | unsigned int (*function_name)(const unsigned char *, const unsigned int, |
882 | const struct ipt_p2p_info *); | |
44d6f47a | 883 | } udp_list[] = { |
a1d307e3 JE |
884 | {IPP2P_KAZAA, 14, udp_search_kazaa}, |
885 | {IPP2P_BIT, 23, udp_search_bit}, | |
886 | {IPP2P_GNU, 11, udp_search_gnu}, | |
887 | {IPP2P_EDK, 9, udp_search_edk}, | |
888 | {IPP2P_DC, 12, udp_search_directconnect}, | |
c237fe24 | 889 | {0}, |
44d6f47a JE |
890 | }; |
891 | ||
ad554f10 | 892 | static void |
f144c2eb JE |
893 | ipp2p_print_result_tcp4(const union nf_inet_addr *saddr, short sport, |
894 | const union nf_inet_addr *daddr, short dport, | |
baa9e72b | 895 | bool p2p_result, unsigned int hlen) |
ad554f10 JS |
896 | { |
897 | printk("IPP2P.debug:TCP-match: %d from: %pI4:%hu to: %pI4:%hu Length: %u\n", | |
898 | p2p_result, &saddr->ip, sport, &daddr->ip, dport, hlen); | |
899 | } | |
900 | ||
baa9e72b | 901 | static void |
f144c2eb JE |
902 | ipp2p_print_result_tcp6(const union nf_inet_addr *saddr, short sport, |
903 | const union nf_inet_addr *daddr, short dport, | |
baa9e72b JE |
904 | bool p2p_result, unsigned int hlen) |
905 | { | |
906 | printk("IPP2P.debug:TCP-match: %d from: %pI6:%hu to: %pI6:%hu Length: %u\n", | |
907 | p2p_result, &saddr->in6, sport, &daddr->in6, dport, hlen); | |
908 | } | |
909 | ||
f68c44f2 JS |
910 | static bool |
911 | ipp2p_mt_tcp(const struct ipt_p2p_info *info, const struct tcphdr *tcph, | |
912 | const unsigned char *haystack, unsigned int hlen, | |
ad554f10 | 913 | const struct ipp2p_result_printer *rp) |
f68c44f2 JS |
914 | { |
915 | size_t tcph_len = tcph->doff * 4; | |
dc05fc62 | 916 | int i; |
f68c44f2 JS |
917 | |
918 | if (tcph->fin) return 0; /* if FIN bit is set bail out */ | |
919 | if (tcph->syn) return 0; /* if SYN bit is set bail out */ | |
920 | if (tcph->rst) return 0; /* if RST bit is set bail out */ | |
921 | ||
922 | if (hlen < tcph_len) { | |
923 | if (info->debug) | |
924 | pr_info("TCP header indicated packet larger than it is\n"); | |
925 | return 0; | |
926 | } | |
927 | if (hlen == tcph_len) | |
928 | return 0; | |
929 | ||
930 | haystack += tcph_len; | |
931 | hlen -= tcph_len; | |
932 | ||
dc05fc62 JS |
933 | for (i = 0; matchlist[i].command; ++i) { |
934 | if ((info->cmd & matchlist[i].command) != matchlist[i].command) | |
935 | continue; | |
936 | if (hlen <= matchlist[i].packet_len) | |
937 | continue; | |
30ba7eb4 | 938 | if (matchlist[i].function_name(haystack, hlen, info)) { |
dc05fc62 JS |
939 | if (info->debug) |
940 | print_result(rp, true, hlen); | |
941 | return true; | |
f68c44f2 | 942 | } |
f68c44f2 | 943 | } |
dc05fc62 | 944 | return false; |
f68c44f2 JS |
945 | } |
946 | ||
ad554f10 | 947 | static void |
f144c2eb JE |
948 | ipp2p_print_result_udp4(const union nf_inet_addr *saddr, short sport, |
949 | const union nf_inet_addr *daddr, short dport, | |
baa9e72b | 950 | bool p2p_result, unsigned int hlen) |
ad554f10 JS |
951 | { |
952 | printk("IPP2P.debug:UDP-match: %d from: %pI4:%hu to: %pI4:%hu Length: %u\n", | |
953 | p2p_result, &saddr->ip, sport, &daddr->ip, dport, hlen); | |
954 | } | |
955 | ||
baa9e72b | 956 | static void |
f144c2eb JE |
957 | ipp2p_print_result_udp6(const union nf_inet_addr *saddr, short sport, |
958 | const union nf_inet_addr *daddr, short dport, | |
baa9e72b JE |
959 | bool p2p_result, unsigned int hlen) |
960 | { | |
961 | printk("IPP2P.debug:UDP-match: %d from: %pI6:%hu to: %pI6:%hu Length: %u\n", | |
962 | p2p_result, &saddr->in6, sport, &daddr->in6, dport, hlen); | |
963 | } | |
964 | ||
f68c44f2 JS |
965 | static bool |
966 | ipp2p_mt_udp(const struct ipt_p2p_info *info, const struct udphdr *udph, | |
967 | const unsigned char *haystack, unsigned int hlen, | |
ad554f10 | 968 | const struct ipp2p_result_printer *rp) |
f68c44f2 JS |
969 | { |
970 | size_t udph_len = sizeof(*udph); | |
dc05fc62 | 971 | int i; |
f68c44f2 JS |
972 | |
973 | if (hlen < udph_len) { | |
974 | if (info->debug) | |
975 | pr_info("UDP header indicated packet larger than it is\n"); | |
976 | return 0; | |
977 | } | |
978 | if (hlen == udph_len) | |
979 | return 0; | |
980 | ||
981 | haystack += udph_len; | |
982 | hlen -= udph_len; | |
983 | ||
dc05fc62 JS |
984 | for (i = 0; udp_list[i].command; ++i) { |
985 | if ((info->cmd & udp_list[i].command) != udp_list[i].command) | |
986 | continue; | |
987 | if (hlen <= udp_list[i].packet_len) | |
988 | continue; | |
30ba7eb4 | 989 | if (udp_list[i].function_name(haystack, hlen, info)) { |
dc05fc62 JS |
990 | if (info->debug) |
991 | print_result(rp, true, hlen); | |
992 | return true; | |
f68c44f2 | 993 | } |
f68c44f2 | 994 | } |
dc05fc62 | 995 | return false; |
f68c44f2 JS |
996 | } |
997 | ||
cc23d0a2 | 998 | static bool |
9a18a05d | 999 | ipp2p_mt(const struct sk_buff *skb, struct xt_action_param *par) |
44d6f47a | 1000 | { |
ee7e4f5a | 1001 | const struct ipt_p2p_info *info = par->matchinfo; |
ad554f10 | 1002 | struct ipp2p_result_printer printer; |
f144c2eb | 1003 | union nf_inet_addr saddr, daddr; |
f68c44f2 JS |
1004 | const unsigned char *haystack; /* packet data */ |
1005 | unsigned int hlen; /* packet data length */ | |
baa9e72b JE |
1006 | uint8_t family = xt_family(par); |
1007 | int protocol; | |
c237fe24 | 1008 | |
baa9e72b JE |
1009 | /* |
1010 | * must not be a fragment | |
1011 | * | |
1012 | * NB, `par->fragoff` may be zero for a fragmented IPv6 packet. | |
1013 | * However, in that case the later call to `ipv6_find_hdr` will not find | |
1014 | * a transport protocol, and so we will return 0 there. | |
1015 | */ | |
ee7e4f5a | 1016 | if (par->fragoff != 0) { |
c237fe24 | 1017 | if (info->debug) |
ee7e4f5a | 1018 | printk("IPP2P.match: offset found %d\n", par->fragoff); |
c237fe24 JE |
1019 | return 0; |
1020 | } | |
1021 | ||
1022 | /* make sure that skb is linear */ | |
1023 | if (skb_is_nonlinear(skb)) { | |
1024 | if (info->debug) | |
1025 | printk("IPP2P.match: nonlinear skb found\n"); | |
1026 | return 0; | |
1027 | } | |
44d6f47a | 1028 | |
baa9e72b JE |
1029 | if (family == NFPROTO_IPV4) { |
1030 | const struct iphdr *ip = ip_hdr(skb); | |
1031 | saddr.ip = ip->saddr; | |
1032 | daddr.ip = ip->daddr; | |
1033 | protocol = ip->protocol; | |
1034 | hlen = ip_transport_len(skb); | |
1035 | } else { | |
1036 | const struct ipv6hdr *ip = ipv6_hdr(skb); | |
1037 | int thoff = 0; | |
44d6f47a | 1038 | |
baa9e72b JE |
1039 | saddr.in6 = ip->saddr; |
1040 | daddr.in6 = ip->daddr; | |
1041 | protocol = ipv6_find_hdr(skb, &thoff, -1, NULL, NULL); | |
1042 | if (protocol < 0) | |
1043 | return 0; | |
1044 | hlen = ipv6_transport_len(skb); | |
1045 | } | |
ad554f10 JS |
1046 | |
1047 | printer.saddr = &saddr; | |
1048 | printer.daddr = &daddr; | |
baa9e72b | 1049 | haystack = skb_transport_header(skb); |
ad554f10 | 1050 | |
baa9e72b | 1051 | switch (protocol) { |
c237fe24 | 1052 | case IPPROTO_TCP: /* what to do with a TCP packet */ |
44d6f47a | 1053 | { |
f68c44f2 | 1054 | const struct tcphdr *tcph = tcp_hdr(skb); |
90835b11 | 1055 | |
ad554f10 JS |
1056 | printer.sport = ntohs(tcph->source); |
1057 | printer.dport = ntohs(tcph->dest); | |
baa9e72b JE |
1058 | printer.print = family == NFPROTO_IPV6 ? |
1059 | ipp2p_print_result_tcp6 : ipp2p_print_result_tcp4; | |
ad554f10 | 1060 | return ipp2p_mt_tcp(info, tcph, haystack, hlen, &printer); |
44d6f47a | 1061 | } |
f68c44f2 | 1062 | case IPPROTO_UDP: /* what to do with a UDP packet */ |
939d3c8b | 1063 | case IPPROTO_UDPLITE: |
44d6f47a | 1064 | { |
f68c44f2 | 1065 | const struct udphdr *udph = udp_hdr(skb); |
c237fe24 | 1066 | |
ad554f10 JS |
1067 | printer.sport = ntohs(udph->source); |
1068 | printer.dport = ntohs(udph->dest); | |
baa9e72b JE |
1069 | printer.print = family == NFPROTO_IPV6 ? |
1070 | ipp2p_print_result_udp6 : ipp2p_print_result_udp4; | |
ad554f10 | 1071 | return ipp2p_mt_udp(info, udph, haystack, hlen, &printer); |
c237fe24 | 1072 | } |
c237fe24 JE |
1073 | default: |
1074 | return 0; | |
44d6f47a | 1075 | } |
44d6f47a JE |
1076 | } |
1077 | ||
30ba7eb4 JS |
1078 | static int ipp2p_mt_check(const struct xt_mtchk_param *par) |
1079 | { | |
1080 | struct ipt_p2p_info *info = par->matchinfo; | |
1081 | struct ts_config *ts_conf; | |
1082 | ||
1083 | ts_conf = textsearch_prepare("bm", "\x20\x22", 2, | |
1084 | GFP_KERNEL, TS_AUTOLOAD); | |
1085 | if (IS_ERR(ts_conf)) | |
1086 | goto err_return; | |
1087 | info->ts_conf_winmx = ts_conf; | |
1088 | ||
1089 | ts_conf = textsearch_prepare("bm", "info_hash=", 10, | |
1090 | GFP_KERNEL, TS_AUTOLOAD); | |
1091 | if (IS_ERR(ts_conf)) | |
1092 | goto err_ts_destroy_winmx; | |
1093 | info->ts_conf_bt_info_hash = ts_conf; | |
1094 | ||
1095 | ts_conf = textsearch_prepare("bm", "peer_id=", 8, | |
1096 | GFP_KERNEL, TS_AUTOLOAD); | |
1097 | if (IS_ERR(ts_conf)) | |
1098 | goto err_ts_destroy_bt_info_hash; | |
1099 | info->ts_conf_bt_peer_id = ts_conf; | |
1100 | ||
1101 | ts_conf = textsearch_prepare("bm", "passkey", 8, | |
1102 | GFP_KERNEL, TS_AUTOLOAD); | |
1103 | if (IS_ERR(ts_conf)) | |
1104 | goto err_ts_destroy_bt_peer_id; | |
1105 | info->ts_conf_bt_passkey = ts_conf; | |
1106 | ||
1107 | ts_conf = textsearch_prepare("bm", "\r\nX-Gnutella-", 13, | |
1108 | GFP_KERNEL, TS_AUTOLOAD); | |
1109 | if (IS_ERR(ts_conf)) | |
1110 | goto err_ts_destroy_bt_passkey; | |
1111 | info->ts_conf_gnu_x_gnutella = ts_conf; | |
1112 | ||
1113 | ts_conf = textsearch_prepare("bm", "\r\nX-Queue-", 10, | |
1114 | GFP_KERNEL, TS_AUTOLOAD); | |
1115 | if (IS_ERR(ts_conf)) | |
1116 | goto err_ts_destroy_gnu_x_gnutella; | |
1117 | info->ts_conf_gnu_x_queue = ts_conf; | |
1118 | ||
1119 | ts_conf = textsearch_prepare("bm", "\r\nX-Kazaa-Username: ", 20, | |
1120 | GFP_KERNEL, TS_AUTOLOAD); | |
1121 | if (IS_ERR(ts_conf)) | |
1122 | goto err_ts_destroy_gnu_x_queue; | |
1123 | info->ts_conf_kz_x_kazaa_username = ts_conf; | |
1124 | ||
1125 | ts_conf = textsearch_prepare("bm", "\r\nUser-Agent: PeerEnabler/", 26, | |
1126 | GFP_KERNEL, TS_AUTOLOAD); | |
1127 | if (IS_ERR(ts_conf)) | |
1128 | goto err_ts_destroy_kazaa_x_kazaa_username; | |
1129 | info->ts_conf_kz_user_agent = ts_conf; | |
1130 | ||
1131 | ts_conf = textsearch_prepare("bm", ":xdcc send #", 12, | |
1132 | GFP_KERNEL, TS_AUTOLOAD); | |
1133 | if (IS_ERR(ts_conf)) | |
1134 | goto err_ts_destroy_kazaa_user_agent; | |
1135 | info->ts_conf_xdcc = ts_conf; | |
1136 | ||
1137 | return 0; | |
1138 | ||
1139 | err_ts_destroy_kazaa_user_agent: | |
1140 | textsearch_destroy(info->ts_conf_kz_user_agent); | |
1141 | ||
1142 | err_ts_destroy_kazaa_x_kazaa_username: | |
1143 | textsearch_destroy(info->ts_conf_kz_x_kazaa_username); | |
1144 | ||
1145 | err_ts_destroy_gnu_x_queue: | |
1146 | textsearch_destroy(info->ts_conf_gnu_x_queue); | |
1147 | ||
1148 | err_ts_destroy_gnu_x_gnutella: | |
1149 | textsearch_destroy(info->ts_conf_gnu_x_gnutella); | |
1150 | ||
1151 | err_ts_destroy_bt_passkey: | |
1152 | textsearch_destroy(info->ts_conf_bt_passkey); | |
1153 | ||
1154 | err_ts_destroy_bt_peer_id: | |
1155 | textsearch_destroy(info->ts_conf_bt_peer_id); | |
1156 | ||
1157 | err_ts_destroy_bt_info_hash: | |
1158 | textsearch_destroy(info->ts_conf_bt_info_hash); | |
1159 | ||
1160 | err_ts_destroy_winmx: | |
1161 | textsearch_destroy(info->ts_conf_winmx); | |
1162 | ||
1163 | err_return: | |
1164 | return PTR_ERR(ts_conf); | |
1165 | } | |
1166 | ||
1167 | static void ipp2p_mt_destroy(const struct xt_mtdtor_param *par) | |
1168 | { | |
1169 | struct ipt_p2p_info *info = (struct ipt_p2p_info *) par->matchinfo; | |
1170 | ||
1171 | textsearch_destroy(info->ts_conf_winmx); | |
1172 | textsearch_destroy(info->ts_conf_bt_info_hash); | |
1173 | textsearch_destroy(info->ts_conf_bt_peer_id); | |
1174 | textsearch_destroy(info->ts_conf_bt_passkey); | |
1175 | textsearch_destroy(info->ts_conf_gnu_x_gnutella); | |
1176 | textsearch_destroy(info->ts_conf_gnu_x_queue); | |
1177 | textsearch_destroy(info->ts_conf_kz_x_kazaa_username); | |
1178 | textsearch_destroy(info->ts_conf_kz_user_agent); | |
1179 | textsearch_destroy(info->ts_conf_xdcc); | |
1180 | } | |
1181 | ||
baa9e72b JE |
1182 | static struct xt_match ipp2p_mt_reg[] __read_mostly = { |
1183 | { | |
1184 | .name = "ipp2p", | |
1185 | .revision = 1, | |
1186 | .family = NFPROTO_IPV4, | |
30ba7eb4 | 1187 | .checkentry = ipp2p_mt_check, |
baa9e72b | 1188 | .match = ipp2p_mt, |
30ba7eb4 | 1189 | .destroy = ipp2p_mt_destroy, |
baa9e72b JE |
1190 | .matchsize = sizeof(struct ipt_p2p_info), |
1191 | .me = THIS_MODULE, | |
1192 | }, | |
1193 | { | |
1194 | .name = "ipp2p", | |
1195 | .revision = 1, | |
1196 | .family = NFPROTO_IPV6, | |
30ba7eb4 | 1197 | .checkentry = ipp2p_mt_check, |
baa9e72b | 1198 | .match = ipp2p_mt, |
30ba7eb4 | 1199 | .destroy = ipp2p_mt_destroy, |
baa9e72b JE |
1200 | .matchsize = sizeof(struct ipt_p2p_info), |
1201 | .me = THIS_MODULE, | |
1202 | }, | |
44d6f47a JE |
1203 | }; |
1204 | ||
cc23d0a2 | 1205 | static int __init ipp2p_mt_init(void) |
44d6f47a | 1206 | { |
baa9e72b | 1207 | return xt_register_matches(ipp2p_mt_reg, ARRAY_SIZE(ipp2p_mt_reg)); |
44d6f47a | 1208 | } |
c237fe24 | 1209 | |
cc23d0a2 | 1210 | static void __exit ipp2p_mt_exit(void) |
44d6f47a | 1211 | { |
baa9e72b | 1212 | xt_unregister_matches(ipp2p_mt_reg, ARRAY_SIZE(ipp2p_mt_reg)); |
44d6f47a | 1213 | } |
c237fe24 | 1214 | |
cc23d0a2 JE |
1215 | module_init(ipp2p_mt_init); |
1216 | module_exit(ipp2p_mt_exit); | |
29139c14 | 1217 | MODULE_ALIAS("ipt_ipp2p"); |
71599a0f | 1218 | MODULE_ALIAS("ip6t_ipp2p"); |