1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* SIP extension for NAT alteration. |
3 | * |
4 | * (C) 2005 by Christian Hentschel <chentschel@arnet.com.ar> |
5 | * based on RR's ip_nat_ftp.c and other modules. |
6 | * (C) 2007 United Security Providers |
7 | * (C) 2007, 2008, 2011, 2012 Patrick McHardy <kaber@trash.net> |
8 | */ |
9 | |
10 | #include <linux/module.h> |
11 | #include <linux/skbuff.h> |
12 | #include <linux/inet.h> |
13 | #include <linux/udp.h> |
14 | #include <linux/tcp.h> |
15 | |
16 | #include <net/netfilter/nf_nat.h> |
17 | #include <net/netfilter/nf_nat_helper.h> |
18 | #include <net/netfilter/nf_conntrack_core.h> |
19 | #include <net/netfilter/nf_conntrack_helper.h> |
20 | #include <net/netfilter/nf_conntrack_expect.h> |
21 | #include <net/netfilter/nf_conntrack_seqadj.h> |
22 | #include <linux/netfilter/nf_conntrack_sip.h> |
23 | |
24 | #define NAT_HELPER_NAME "sip" |
25 | |
26 | MODULE_LICENSE("GPL" ); |
27 | MODULE_AUTHOR("Christian Hentschel <chentschel@arnet.com.ar>" ); |
28 | MODULE_DESCRIPTION("SIP NAT helper" ); |
29 | MODULE_ALIAS_NF_NAT_HELPER(NAT_HELPER_NAME); |
30 | |
31 | static struct nf_conntrack_nat_helper nat_helper_sip = |
32 | NF_CT_NAT_HELPER_INIT(NAT_HELPER_NAME); |
33 | |
34 | static unsigned int mangle_packet(struct sk_buff *skb, unsigned int protoff, |
35 | unsigned int dataoff, |
36 | const char **dptr, unsigned int *datalen, |
37 | unsigned int matchoff, unsigned int matchlen, |
38 | const char *buffer, unsigned int buflen) |
39 | { |
40 | enum ip_conntrack_info ctinfo; |
41 | struct nf_conn *ct = nf_ct_get(skb, ctinfo: &ctinfo); |
42 | struct tcphdr *th; |
43 | unsigned int baseoff; |
44 | |
45 | if (nf_ct_protonum(ct) == IPPROTO_TCP) { |
46 | th = (struct tcphdr *)(skb->data + protoff); |
47 | baseoff = protoff + th->doff * 4; |
48 | matchoff += dataoff - baseoff; |
49 | |
50 | if (!__nf_nat_mangle_tcp_packet(skb, ct, ctinfo, |
51 | protoff, match_offset: matchoff, match_len: matchlen, |
52 | rep_buffer: buffer, rep_len: buflen, adjust: false)) |
53 | return 0; |
54 | } else { |
55 | baseoff = protoff + sizeof(struct udphdr); |
56 | matchoff += dataoff - baseoff; |
57 | |
58 | if (!nf_nat_mangle_udp_packet(skb, ct, ctinfo, |
59 | protoff, match_offset: matchoff, match_len: matchlen, |
60 | rep_buffer: buffer, rep_len: buflen)) |
61 | return 0; |
62 | } |
63 | |
64 | /* Reload data pointer and adjust datalen value */ |
65 | *dptr = skb->data + dataoff; |
66 | *datalen += buflen - matchlen; |
67 | return 1; |
68 | } |
69 | |
70 | static int sip_sprintf_addr(const struct nf_conn *ct, char *buffer, |
71 | const union nf_inet_addr *addr, bool delim) |
72 | { |
73 | if (nf_ct_l3num(ct) == NFPROTO_IPV4) |
74 | return sprintf(buf: buffer, fmt: "%pI4" , &addr->ip); |
75 | else { |
76 | if (delim) |
77 | return sprintf(buf: buffer, fmt: "[%pI6c]" , &addr->ip6); |
78 | else |
79 | return sprintf(buf: buffer, fmt: "%pI6c" , &addr->ip6); |
80 | } |
81 | } |
82 | |
83 | static int sip_sprintf_addr_port(const struct nf_conn *ct, char *buffer, |
84 | const union nf_inet_addr *addr, u16 port) |
85 | { |
86 | if (nf_ct_l3num(ct) == NFPROTO_IPV4) |
87 | return sprintf(buf: buffer, fmt: "%pI4:%u" , &addr->ip, port); |
88 | else |
89 | return sprintf(buf: buffer, fmt: "[%pI6c]:%u" , &addr->ip6, port); |
90 | } |
91 | |
92 | static int map_addr(struct sk_buff *skb, unsigned int protoff, |
93 | unsigned int dataoff, |
94 | const char **dptr, unsigned int *datalen, |
95 | unsigned int matchoff, unsigned int matchlen, |
96 | union nf_inet_addr *addr, __be16 port) |
97 | { |
98 | enum ip_conntrack_info ctinfo; |
99 | struct nf_conn *ct = nf_ct_get(skb, ctinfo: &ctinfo); |
100 | enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo); |
101 | struct nf_ct_sip_master *ct_sip_info = nfct_help_data(ct); |
102 | char buffer[INET6_ADDRSTRLEN + sizeof("[]:nnnnn" )]; |
103 | unsigned int buflen; |
104 | union nf_inet_addr newaddr; |
105 | __be16 newport; |
106 | |
107 | if (nf_inet_addr_cmp(a1: &ct->tuplehash[dir].tuple.src.u3, a2: addr) && |
108 | ct->tuplehash[dir].tuple.src.u.udp.port == port) { |
109 | newaddr = ct->tuplehash[!dir].tuple.dst.u3; |
110 | newport = ct->tuplehash[!dir].tuple.dst.u.udp.port; |
111 | } else if (nf_inet_addr_cmp(a1: &ct->tuplehash[dir].tuple.dst.u3, a2: addr) && |
112 | ct->tuplehash[dir].tuple.dst.u.udp.port == port) { |
113 | newaddr = ct->tuplehash[!dir].tuple.src.u3; |
114 | newport = ct_sip_info->forced_dport ? : |
115 | ct->tuplehash[!dir].tuple.src.u.udp.port; |
116 | } else |
117 | return 1; |
118 | |
119 | if (nf_inet_addr_cmp(a1: &newaddr, a2: addr) && newport == port) |
120 | return 1; |
121 | |
122 | buflen = sip_sprintf_addr_port(ct, buffer, addr: &newaddr, ntohs(newport)); |
123 | return mangle_packet(skb, protoff, dataoff, dptr, datalen, |
124 | matchoff, matchlen, buffer, buflen); |
125 | } |
126 | |
127 | static int map_sip_addr(struct sk_buff *skb, unsigned int protoff, |
128 | unsigned int dataoff, |
129 | const char **dptr, unsigned int *datalen, |
130 | enum sip_header_types type) |
131 | { |
132 | enum ip_conntrack_info ctinfo; |
133 | struct nf_conn *ct = nf_ct_get(skb, ctinfo: &ctinfo); |
134 | unsigned int matchlen, matchoff; |
135 | union nf_inet_addr addr; |
136 | __be16 port; |
137 | |
138 | if (ct_sip_parse_header_uri(ct, dptr: *dptr, NULL, datalen: *datalen, type, NULL, |
139 | matchoff: &matchoff, matchlen: &matchlen, addr: &addr, port: &port) <= 0) |
140 | return 1; |
141 | return map_addr(skb, protoff, dataoff, dptr, datalen, |
142 | matchoff, matchlen, addr: &addr, port); |
143 | } |
144 | |
145 | static unsigned int nf_nat_sip(struct sk_buff *skb, unsigned int protoff, |
146 | unsigned int dataoff, |
147 | const char **dptr, unsigned int *datalen) |
148 | { |
149 | enum ip_conntrack_info ctinfo; |
150 | struct nf_conn *ct = nf_ct_get(skb, ctinfo: &ctinfo); |
151 | enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo); |
152 | struct nf_ct_sip_master *ct_sip_info = nfct_help_data(ct); |
153 | unsigned int coff, matchoff, matchlen; |
154 | enum sip_header_types hdr; |
155 | union nf_inet_addr addr; |
156 | __be16 port; |
157 | int request, ; |
158 | |
159 | /* Basic rules: requests and responses. */ |
160 | if (strncasecmp(s1: *dptr, s2: "SIP/2.0" , strlen("SIP/2.0" )) != 0) { |
161 | if (ct_sip_parse_request(ct, dptr: *dptr, datalen: *datalen, |
162 | matchoff: &matchoff, matchlen: &matchlen, |
163 | addr: &addr, port: &port) > 0 && |
164 | !map_addr(skb, protoff, dataoff, dptr, datalen, |
165 | matchoff, matchlen, addr: &addr, port)) { |
166 | nf_ct_helper_log(skb, ct, fmt: "cannot mangle SIP message" ); |
167 | return NF_DROP; |
168 | } |
169 | request = 1; |
170 | } else |
171 | request = 0; |
172 | |
173 | if (nf_ct_protonum(ct) == IPPROTO_TCP) |
174 | hdr = SIP_HDR_VIA_TCP; |
175 | else |
176 | hdr = SIP_HDR_VIA_UDP; |
177 | |
178 | /* Translate topmost Via header and parameters */ |
179 | if (ct_sip_parse_header_uri(ct, dptr: *dptr, NULL, datalen: *datalen, |
180 | type: hdr, NULL, matchoff: &matchoff, matchlen: &matchlen, |
181 | addr: &addr, port: &port) > 0) { |
182 | unsigned int olen, matchend, poff, plen, buflen, n; |
183 | char buffer[INET6_ADDRSTRLEN + sizeof("[]:nnnnn" )]; |
184 | |
185 | /* We're only interested in headers related to this |
186 | * connection */ |
187 | if (request) { |
188 | if (!nf_inet_addr_cmp(a1: &addr, |
189 | a2: &ct->tuplehash[dir].tuple.src.u3) || |
190 | port != ct->tuplehash[dir].tuple.src.u.udp.port) |
191 | goto next; |
192 | } else { |
193 | if (!nf_inet_addr_cmp(a1: &addr, |
194 | a2: &ct->tuplehash[dir].tuple.dst.u3) || |
195 | port != ct->tuplehash[dir].tuple.dst.u.udp.port) |
196 | goto next; |
197 | } |
198 | |
199 | olen = *datalen; |
200 | if (!map_addr(skb, protoff, dataoff, dptr, datalen, |
201 | matchoff, matchlen, addr: &addr, port)) { |
202 | nf_ct_helper_log(skb, ct, fmt: "cannot mangle Via header" ); |
203 | return NF_DROP; |
204 | } |
205 | |
206 | matchend = matchoff + matchlen + *datalen - olen; |
207 | |
208 | /* The maddr= parameter (RFC 2361) specifies where to send |
209 | * the reply. */ |
210 | if (ct_sip_parse_address_param(ct, dptr: *dptr, dataoff: matchend, datalen: *datalen, |
211 | name: "maddr=" , matchoff: &poff, matchlen: &plen, |
212 | addr: &addr, delim: true) > 0 && |
213 | nf_inet_addr_cmp(a1: &addr, a2: &ct->tuplehash[dir].tuple.src.u3) && |
214 | !nf_inet_addr_cmp(a1: &addr, a2: &ct->tuplehash[!dir].tuple.dst.u3)) { |
215 | buflen = sip_sprintf_addr(ct, buffer, |
216 | addr: &ct->tuplehash[!dir].tuple.dst.u3, |
217 | delim: true); |
218 | if (!mangle_packet(skb, protoff, dataoff, dptr, datalen, |
219 | matchoff: poff, matchlen: plen, buffer, buflen)) { |
220 | nf_ct_helper_log(skb, ct, fmt: "cannot mangle maddr" ); |
221 | return NF_DROP; |
222 | } |
223 | } |
224 | |
225 | /* The received= parameter (RFC 2361) contains the address |
226 | * from which the server received the request. */ |
227 | if (ct_sip_parse_address_param(ct, dptr: *dptr, dataoff: matchend, datalen: *datalen, |
228 | name: "received=" , matchoff: &poff, matchlen: &plen, |
229 | addr: &addr, delim: false) > 0 && |
230 | nf_inet_addr_cmp(a1: &addr, a2: &ct->tuplehash[dir].tuple.dst.u3) && |
231 | !nf_inet_addr_cmp(a1: &addr, a2: &ct->tuplehash[!dir].tuple.src.u3)) { |
232 | buflen = sip_sprintf_addr(ct, buffer, |
233 | addr: &ct->tuplehash[!dir].tuple.src.u3, |
234 | delim: false); |
235 | if (!mangle_packet(skb, protoff, dataoff, dptr, datalen, |
236 | matchoff: poff, matchlen: plen, buffer, buflen)) { |
237 | nf_ct_helper_log(skb, ct, fmt: "cannot mangle received" ); |
238 | return NF_DROP; |
239 | } |
240 | } |
241 | |
242 | /* The rport= parameter (RFC 3581) contains the port number |
243 | * from which the server received the request. */ |
244 | if (ct_sip_parse_numerical_param(ct, dptr: *dptr, off: matchend, datalen: *datalen, |
245 | name: "rport=" , matchoff: &poff, matchen: &plen, |
246 | val: &n) > 0 && |
247 | htons(n) == ct->tuplehash[dir].tuple.dst.u.udp.port && |
248 | htons(n) != ct->tuplehash[!dir].tuple.src.u.udp.port) { |
249 | __be16 p = ct->tuplehash[!dir].tuple.src.u.udp.port; |
250 | buflen = sprintf(buf: buffer, fmt: "%u" , ntohs(p)); |
251 | if (!mangle_packet(skb, protoff, dataoff, dptr, datalen, |
252 | matchoff: poff, matchlen: plen, buffer, buflen)) { |
253 | nf_ct_helper_log(skb, ct, fmt: "cannot mangle rport" ); |
254 | return NF_DROP; |
255 | } |
256 | } |
257 | } |
258 | |
259 | next: |
260 | /* Translate Contact headers */ |
261 | coff = 0; |
262 | in_header = 0; |
263 | while (ct_sip_parse_header_uri(ct, dptr: *dptr, dataoff: &coff, datalen: *datalen, |
264 | type: SIP_HDR_CONTACT, in_header: &in_header, |
265 | matchoff: &matchoff, matchlen: &matchlen, |
266 | addr: &addr, port: &port) > 0) { |
267 | if (!map_addr(skb, protoff, dataoff, dptr, datalen, |
268 | matchoff, matchlen, |
269 | addr: &addr, port)) { |
270 | nf_ct_helper_log(skb, ct, fmt: "cannot mangle contact" ); |
271 | return NF_DROP; |
272 | } |
273 | } |
274 | |
275 | if (!map_sip_addr(skb, protoff, dataoff, dptr, datalen, type: SIP_HDR_FROM) || |
276 | !map_sip_addr(skb, protoff, dataoff, dptr, datalen, type: SIP_HDR_TO)) { |
277 | nf_ct_helper_log(skb, ct, fmt: "cannot mangle SIP from/to" ); |
278 | return NF_DROP; |
279 | } |
280 | |
281 | /* Mangle destination port for Cisco phones, then fix up checksums */ |
282 | if (dir == IP_CT_DIR_REPLY && ct_sip_info->forced_dport) { |
283 | struct udphdr *uh; |
284 | |
285 | if (skb_ensure_writable(skb, write_len: skb->len)) { |
286 | nf_ct_helper_log(skb, ct, fmt: "cannot mangle packet" ); |
287 | return NF_DROP; |
288 | } |
289 | |
290 | uh = (void *)skb->data + protoff; |
291 | uh->dest = ct_sip_info->forced_dport; |
292 | |
293 | if (!nf_nat_mangle_udp_packet(skb, ct, ctinfo, protoff, |
294 | match_offset: 0, match_len: 0, NULL, rep_len: 0)) { |
295 | nf_ct_helper_log(skb, ct, fmt: "cannot mangle packet" ); |
296 | return NF_DROP; |
297 | } |
298 | } |
299 | |
300 | return NF_ACCEPT; |
301 | } |
302 | |
303 | static void nf_nat_sip_seq_adjust(struct sk_buff *skb, unsigned int protoff, |
304 | s16 off) |
305 | { |
306 | enum ip_conntrack_info ctinfo; |
307 | struct nf_conn *ct = nf_ct_get(skb, ctinfo: &ctinfo); |
308 | const struct tcphdr *th; |
309 | |
310 | if (nf_ct_protonum(ct) != IPPROTO_TCP || off == 0) |
311 | return; |
312 | |
313 | th = (struct tcphdr *)(skb->data + protoff); |
314 | nf_ct_seqadj_set(ct, ctinfo, seq: th->seq, off); |
315 | } |
316 | |
317 | /* Handles expected signalling connections and media streams */ |
318 | static void nf_nat_sip_expected(struct nf_conn *ct, |
319 | struct nf_conntrack_expect *exp) |
320 | { |
321 | struct nf_conn_help *help = nfct_help(ct: ct->master); |
322 | struct nf_conntrack_expect *pair_exp; |
323 | int range_set_for_snat = 0; |
324 | struct nf_nat_range2 range; |
325 | |
326 | /* This must be a fresh one. */ |
327 | BUG_ON(ct->status & IPS_NAT_DONE_MASK); |
328 | |
329 | /* For DST manip, map port here to where it's expected. */ |
330 | range.flags = (NF_NAT_RANGE_MAP_IPS | NF_NAT_RANGE_PROTO_SPECIFIED); |
331 | range.min_proto = range.max_proto = exp->saved_proto; |
332 | range.min_addr = range.max_addr = exp->saved_addr; |
333 | nf_nat_setup_info(ct, range: &range, maniptype: NF_NAT_MANIP_DST); |
334 | |
335 | /* Do media streams SRC manip according with the parameters |
336 | * found in the paired expectation. |
337 | */ |
338 | if (exp->class != SIP_EXPECT_SIGNALLING) { |
339 | spin_lock_bh(lock: &nf_conntrack_expect_lock); |
340 | hlist_for_each_entry(pair_exp, &help->expectations, lnode) { |
341 | if (pair_exp->tuple.src.l3num == nf_ct_l3num(ct) && |
342 | pair_exp->tuple.dst.protonum == ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.protonum && |
343 | nf_inet_addr_cmp(a1: &ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.u3, a2: &pair_exp->saved_addr) && |
344 | ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.u.all == pair_exp->saved_proto.all) { |
345 | range.flags = (NF_NAT_RANGE_MAP_IPS | NF_NAT_RANGE_PROTO_SPECIFIED); |
346 | range.min_proto.all = range.max_proto.all = pair_exp->tuple.dst.u.all; |
347 | range.min_addr = range.max_addr = pair_exp->tuple.dst.u3; |
348 | range_set_for_snat = 1; |
349 | break; |
350 | } |
351 | } |
352 | spin_unlock_bh(lock: &nf_conntrack_expect_lock); |
353 | } |
354 | |
355 | /* When no paired expectation has been found, change src to |
356 | * where master sends to, but only if the connection actually came |
357 | * from the same source. |
358 | */ |
359 | if (!range_set_for_snat && |
360 | nf_inet_addr_cmp(a1: &ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.u3, |
361 | a2: &ct->master->tuplehash[exp->dir].tuple.src.u3)) { |
362 | range.flags = NF_NAT_RANGE_MAP_IPS; |
363 | range.min_addr = range.max_addr |
364 | = ct->master->tuplehash[!exp->dir].tuple.dst.u3; |
365 | range_set_for_snat = 1; |
366 | } |
367 | |
368 | /* Perform SRC manip. */ |
369 | if (range_set_for_snat) |
370 | nf_nat_setup_info(ct, range: &range, maniptype: NF_NAT_MANIP_SRC); |
371 | } |
372 | |
373 | static unsigned int nf_nat_sip_expect(struct sk_buff *skb, unsigned int protoff, |
374 | unsigned int dataoff, |
375 | const char **dptr, unsigned int *datalen, |
376 | struct nf_conntrack_expect *exp, |
377 | unsigned int matchoff, |
378 | unsigned int matchlen) |
379 | { |
380 | enum ip_conntrack_info ctinfo; |
381 | struct nf_conn *ct = nf_ct_get(skb, ctinfo: &ctinfo); |
382 | enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo); |
383 | struct nf_ct_sip_master *ct_sip_info = nfct_help_data(ct); |
384 | union nf_inet_addr newaddr; |
385 | u_int16_t port; |
386 | __be16 srcport; |
387 | char buffer[INET6_ADDRSTRLEN + sizeof("[]:nnnnn" )]; |
388 | unsigned int buflen; |
389 | |
390 | /* Connection will come from reply */ |
391 | if (nf_inet_addr_cmp(a1: &ct->tuplehash[dir].tuple.src.u3, |
392 | a2: &ct->tuplehash[!dir].tuple.dst.u3)) |
393 | newaddr = exp->tuple.dst.u3; |
394 | else |
395 | newaddr = ct->tuplehash[!dir].tuple.dst.u3; |
396 | |
397 | /* If the signalling port matches the connection's source port in the |
398 | * original direction, try to use the destination port in the opposite |
399 | * direction. */ |
400 | srcport = ct_sip_info->forced_dport ? : |
401 | ct->tuplehash[dir].tuple.src.u.udp.port; |
402 | if (exp->tuple.dst.u.udp.port == srcport) |
403 | port = ntohs(ct->tuplehash[!dir].tuple.dst.u.udp.port); |
404 | else |
405 | port = ntohs(exp->tuple.dst.u.udp.port); |
406 | |
407 | exp->saved_addr = exp->tuple.dst.u3; |
408 | exp->tuple.dst.u3 = newaddr; |
409 | exp->saved_proto.udp.port = exp->tuple.dst.u.udp.port; |
410 | exp->dir = !dir; |
411 | exp->expectfn = nf_nat_sip_expected; |
412 | |
413 | port = nf_nat_exp_find_port(exp, port); |
414 | if (port == 0) { |
415 | nf_ct_helper_log(skb, ct, fmt: "all ports in use for SIP" ); |
416 | return NF_DROP; |
417 | } |
418 | |
419 | if (!nf_inet_addr_cmp(a1: &exp->tuple.dst.u3, a2: &exp->saved_addr) || |
420 | exp->tuple.dst.u.udp.port != exp->saved_proto.udp.port) { |
421 | buflen = sip_sprintf_addr_port(ct, buffer, addr: &newaddr, port); |
422 | if (!mangle_packet(skb, protoff, dataoff, dptr, datalen, |
423 | matchoff, matchlen, buffer, buflen)) { |
424 | nf_ct_helper_log(skb, ct, fmt: "cannot mangle packet" ); |
425 | goto err; |
426 | } |
427 | } |
428 | return NF_ACCEPT; |
429 | |
430 | err: |
431 | nf_ct_unexpect_related(exp); |
432 | return NF_DROP; |
433 | } |
434 | |
435 | static int mangle_content_len(struct sk_buff *skb, unsigned int protoff, |
436 | unsigned int dataoff, |
437 | const char **dptr, unsigned int *datalen) |
438 | { |
439 | enum ip_conntrack_info ctinfo; |
440 | struct nf_conn *ct = nf_ct_get(skb, ctinfo: &ctinfo); |
441 | unsigned int matchoff, matchlen; |
442 | char buffer[sizeof("65536" )]; |
443 | int buflen, c_len; |
444 | |
445 | /* Get actual SDP length */ |
446 | if (ct_sip_get_sdp_header(ct, dptr: *dptr, dataoff: 0, datalen: *datalen, |
447 | type: SDP_HDR_VERSION, term: SDP_HDR_UNSPEC, |
448 | matchoff: &matchoff, matchlen: &matchlen) <= 0) |
449 | return 0; |
450 | c_len = *datalen - matchoff + strlen("v=" ); |
451 | |
452 | /* Now, update SDP length */ |
453 | if (ct_sip_get_header(ct, dptr: *dptr, dataoff: 0, datalen: *datalen, type: SIP_HDR_CONTENT_LENGTH, |
454 | matchoff: &matchoff, matchlen: &matchlen) <= 0) |
455 | return 0; |
456 | |
457 | buflen = sprintf(buf: buffer, fmt: "%u" , c_len); |
458 | return mangle_packet(skb, protoff, dataoff, dptr, datalen, |
459 | matchoff, matchlen, buffer, buflen); |
460 | } |
461 | |
462 | static int mangle_sdp_packet(struct sk_buff *skb, unsigned int protoff, |
463 | unsigned int dataoff, |
464 | const char **dptr, unsigned int *datalen, |
465 | unsigned int sdpoff, |
466 | enum sdp_header_types type, |
467 | enum sdp_header_types term, |
468 | char *buffer, int buflen) |
469 | { |
470 | enum ip_conntrack_info ctinfo; |
471 | struct nf_conn *ct = nf_ct_get(skb, ctinfo: &ctinfo); |
472 | unsigned int matchlen, matchoff; |
473 | |
474 | if (ct_sip_get_sdp_header(ct, dptr: *dptr, dataoff: sdpoff, datalen: *datalen, type, term, |
475 | matchoff: &matchoff, matchlen: &matchlen) <= 0) |
476 | return -ENOENT; |
477 | return mangle_packet(skb, protoff, dataoff, dptr, datalen, |
478 | matchoff, matchlen, buffer, buflen) ? 0 : -EINVAL; |
479 | } |
480 | |
481 | static unsigned int nf_nat_sdp_addr(struct sk_buff *skb, unsigned int protoff, |
482 | unsigned int dataoff, |
483 | const char **dptr, unsigned int *datalen, |
484 | unsigned int sdpoff, |
485 | enum sdp_header_types type, |
486 | enum sdp_header_types term, |
487 | const union nf_inet_addr *addr) |
488 | { |
489 | enum ip_conntrack_info ctinfo; |
490 | struct nf_conn *ct = nf_ct_get(skb, ctinfo: &ctinfo); |
491 | char buffer[INET6_ADDRSTRLEN]; |
492 | unsigned int buflen; |
493 | |
494 | buflen = sip_sprintf_addr(ct, buffer, addr, delim: false); |
495 | if (mangle_sdp_packet(skb, protoff, dataoff, dptr, datalen, |
496 | sdpoff, type, term, buffer, buflen)) |
497 | return 0; |
498 | |
499 | return mangle_content_len(skb, protoff, dataoff, dptr, datalen); |
500 | } |
501 | |
502 | static unsigned int nf_nat_sdp_port(struct sk_buff *skb, unsigned int protoff, |
503 | unsigned int dataoff, |
504 | const char **dptr, unsigned int *datalen, |
505 | unsigned int matchoff, |
506 | unsigned int matchlen, |
507 | u_int16_t port) |
508 | { |
509 | char buffer[sizeof("nnnnn" )]; |
510 | unsigned int buflen; |
511 | |
512 | buflen = sprintf(buf: buffer, fmt: "%u" , port); |
513 | if (!mangle_packet(skb, protoff, dataoff, dptr, datalen, |
514 | matchoff, matchlen, buffer, buflen)) |
515 | return 0; |
516 | |
517 | return mangle_content_len(skb, protoff, dataoff, dptr, datalen); |
518 | } |
519 | |
520 | static unsigned int nf_nat_sdp_session(struct sk_buff *skb, unsigned int protoff, |
521 | unsigned int dataoff, |
522 | const char **dptr, unsigned int *datalen, |
523 | unsigned int sdpoff, |
524 | const union nf_inet_addr *addr) |
525 | { |
526 | enum ip_conntrack_info ctinfo; |
527 | struct nf_conn *ct = nf_ct_get(skb, ctinfo: &ctinfo); |
528 | char buffer[INET6_ADDRSTRLEN]; |
529 | unsigned int buflen; |
530 | |
531 | /* Mangle session description owner and contact addresses */ |
532 | buflen = sip_sprintf_addr(ct, buffer, addr, delim: false); |
533 | if (mangle_sdp_packet(skb, protoff, dataoff, dptr, datalen, sdpoff, |
534 | type: SDP_HDR_OWNER, term: SDP_HDR_MEDIA, buffer, buflen)) |
535 | return 0; |
536 | |
537 | switch (mangle_sdp_packet(skb, protoff, dataoff, dptr, datalen, sdpoff, |
538 | type: SDP_HDR_CONNECTION, term: SDP_HDR_MEDIA, |
539 | buffer, buflen)) { |
540 | case 0: |
541 | /* |
542 | * RFC 2327: |
543 | * |
544 | * Session description |
545 | * |
546 | * c=* (connection information - not required if included in all media) |
547 | */ |
548 | case -ENOENT: |
549 | break; |
550 | default: |
551 | return 0; |
552 | } |
553 | |
554 | return mangle_content_len(skb, protoff, dataoff, dptr, datalen); |
555 | } |
556 | |
557 | /* So, this packet has hit the connection tracking matching code. |
558 | Mangle it, and change the expectation to match the new version. */ |
559 | static unsigned int nf_nat_sdp_media(struct sk_buff *skb, unsigned int protoff, |
560 | unsigned int dataoff, |
561 | const char **dptr, unsigned int *datalen, |
562 | struct nf_conntrack_expect *rtp_exp, |
563 | struct nf_conntrack_expect *rtcp_exp, |
564 | unsigned int mediaoff, |
565 | unsigned int medialen, |
566 | union nf_inet_addr *rtp_addr) |
567 | { |
568 | enum ip_conntrack_info ctinfo; |
569 | struct nf_conn *ct = nf_ct_get(skb, ctinfo: &ctinfo); |
570 | enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo); |
571 | u_int16_t port; |
572 | |
573 | /* Connection will come from reply */ |
574 | if (nf_inet_addr_cmp(a1: &ct->tuplehash[dir].tuple.src.u3, |
575 | a2: &ct->tuplehash[!dir].tuple.dst.u3)) |
576 | *rtp_addr = rtp_exp->tuple.dst.u3; |
577 | else |
578 | *rtp_addr = ct->tuplehash[!dir].tuple.dst.u3; |
579 | |
580 | rtp_exp->saved_addr = rtp_exp->tuple.dst.u3; |
581 | rtp_exp->tuple.dst.u3 = *rtp_addr; |
582 | rtp_exp->saved_proto.udp.port = rtp_exp->tuple.dst.u.udp.port; |
583 | rtp_exp->dir = !dir; |
584 | rtp_exp->expectfn = nf_nat_sip_expected; |
585 | |
586 | rtcp_exp->saved_addr = rtcp_exp->tuple.dst.u3; |
587 | rtcp_exp->tuple.dst.u3 = *rtp_addr; |
588 | rtcp_exp->saved_proto.udp.port = rtcp_exp->tuple.dst.u.udp.port; |
589 | rtcp_exp->dir = !dir; |
590 | rtcp_exp->expectfn = nf_nat_sip_expected; |
591 | |
592 | /* Try to get same pair of ports: if not, try to change them. */ |
593 | for (port = ntohs(rtp_exp->tuple.dst.u.udp.port); |
594 | port != 0; port += 2) { |
595 | int ret; |
596 | |
597 | rtp_exp->tuple.dst.u.udp.port = htons(port); |
598 | ret = nf_ct_expect_related(expect: rtp_exp, |
599 | NF_CT_EXP_F_SKIP_MASTER); |
600 | if (ret == -EBUSY) |
601 | continue; |
602 | else if (ret < 0) { |
603 | port = 0; |
604 | break; |
605 | } |
606 | rtcp_exp->tuple.dst.u.udp.port = htons(port + 1); |
607 | ret = nf_ct_expect_related(expect: rtcp_exp, |
608 | NF_CT_EXP_F_SKIP_MASTER); |
609 | if (ret == 0) |
610 | break; |
611 | else if (ret == -EBUSY) { |
612 | nf_ct_unexpect_related(exp: rtp_exp); |
613 | continue; |
614 | } else if (ret < 0) { |
615 | nf_ct_unexpect_related(exp: rtp_exp); |
616 | port = 0; |
617 | break; |
618 | } |
619 | } |
620 | |
621 | if (port == 0) { |
622 | nf_ct_helper_log(skb, ct, fmt: "all ports in use for SDP media" ); |
623 | goto err1; |
624 | } |
625 | |
626 | /* Update media port. */ |
627 | if (rtp_exp->tuple.dst.u.udp.port != rtp_exp->saved_proto.udp.port && |
628 | !nf_nat_sdp_port(skb, protoff, dataoff, dptr, datalen, |
629 | matchoff: mediaoff, matchlen: medialen, port)) { |
630 | nf_ct_helper_log(skb, ct, fmt: "cannot mangle SDP message" ); |
631 | goto err2; |
632 | } |
633 | |
634 | return NF_ACCEPT; |
635 | |
636 | err2: |
637 | nf_ct_unexpect_related(exp: rtp_exp); |
638 | nf_ct_unexpect_related(exp: rtcp_exp); |
639 | err1: |
640 | return NF_DROP; |
641 | } |
642 | |
643 | static struct nf_ct_helper_expectfn sip_nat = { |
644 | .name = "sip" , |
645 | .expectfn = nf_nat_sip_expected, |
646 | }; |
647 | |
648 | static void __exit nf_nat_sip_fini(void) |
649 | { |
650 | nf_nat_helper_unregister(nat: &nat_helper_sip); |
651 | RCU_INIT_POINTER(nf_nat_sip_hooks, NULL); |
652 | nf_ct_helper_expectfn_unregister(n: &sip_nat); |
653 | synchronize_rcu(); |
654 | } |
655 | |
656 | static const struct nf_nat_sip_hooks sip_hooks = { |
657 | .msg = nf_nat_sip, |
658 | .seq_adjust = nf_nat_sip_seq_adjust, |
659 | .expect = nf_nat_sip_expect, |
660 | .sdp_addr = nf_nat_sdp_addr, |
661 | .sdp_port = nf_nat_sdp_port, |
662 | .sdp_session = nf_nat_sdp_session, |
663 | .sdp_media = nf_nat_sdp_media, |
664 | }; |
665 | |
666 | static int __init nf_nat_sip_init(void) |
667 | { |
668 | BUG_ON(nf_nat_sip_hooks != NULL); |
669 | nf_nat_helper_register(nat: &nat_helper_sip); |
670 | RCU_INIT_POINTER(nf_nat_sip_hooks, &sip_hooks); |
671 | nf_ct_helper_expectfn_register(n: &sip_nat); |
672 | return 0; |
673 | } |
674 | |
675 | module_init(nf_nat_sip_init); |
676 | module_exit(nf_nat_sip_fini); |
677 | |