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