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