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