1 /* SIP extension for UDP NAT alteration.
3 * (C) 2005 by Christian Hentschel <chentschel@arnet.com.ar>
4 * based on RR's ip_nat_ftp.c and other modules.
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License version 2 as
8 * published by the Free Software Foundation.
11 #include <linux/module.h>
12 #include <linux/skbuff.h>
15 #include <linux/udp.h>
17 #include <net/netfilter/nf_nat.h>
18 #include <net/netfilter/nf_nat_helper.h>
19 #include <net/netfilter/nf_nat_rule.h>
20 #include <net/netfilter/nf_conntrack_helper.h>
21 #include <net/netfilter/nf_conntrack_expect.h>
22 #include <linux/netfilter/nf_conntrack_sip.h>
24 MODULE_LICENSE("GPL");
25 MODULE_AUTHOR("Christian Hentschel <chentschel@arnet.com.ar>");
26 MODULE_DESCRIPTION("SIP NAT helper");
27 MODULE_ALIAS("ip_nat_sip");
31 char src
[sizeof("nnn.nnn.nnn.nnn:nnnnn")];
32 char dst
[sizeof("nnn.nnn.nnn.nnn:nnnnn")];
33 unsigned int srclen
, srciplen
;
34 unsigned int dstlen
, dstiplen
;
35 } addr
[IP_CT_DIR_MAX
];
38 static void addr_map_init(const struct nf_conn
*ct
, struct addr_map
*map
)
40 const struct nf_conntrack_tuple
*t
;
41 enum ip_conntrack_dir dir
;
44 for (dir
= 0; dir
< IP_CT_DIR_MAX
; dir
++) {
45 t
= &ct
->tuplehash
[dir
].tuple
;
47 n
= sprintf(map
->addr
[dir
].src
, "%u.%u.%u.%u",
48 NIPQUAD(t
->src
.u3
.ip
));
49 map
->addr
[dir
].srciplen
= n
;
50 n
+= sprintf(map
->addr
[dir
].src
+ n
, ":%u",
51 ntohs(t
->src
.u
.udp
.port
));
52 map
->addr
[dir
].srclen
= n
;
54 n
= sprintf(map
->addr
[dir
].dst
, "%u.%u.%u.%u",
55 NIPQUAD(t
->dst
.u3
.ip
));
56 map
->addr
[dir
].dstiplen
= n
;
57 n
+= sprintf(map
->addr
[dir
].dst
+ n
, ":%u",
58 ntohs(t
->dst
.u
.udp
.port
));
59 map
->addr
[dir
].dstlen
= n
;
63 static unsigned int mangle_packet(struct sk_buff
*skb
,
64 const char **dptr
, unsigned int *datalen
,
65 unsigned int matchoff
, unsigned int matchlen
,
66 const char *buffer
, unsigned int buflen
)
68 enum ip_conntrack_info ctinfo
;
69 struct nf_conn
*ct
= nf_ct_get(skb
, &ctinfo
);
71 if (!nf_nat_mangle_udp_packet(skb
, ct
, ctinfo
, matchoff
, matchlen
,
75 /* Reload data pointer and adjust datalen value */
76 *dptr
= skb
->data
+ ip_hdrlen(skb
) + sizeof(struct udphdr
);
77 *datalen
+= buflen
- matchlen
;
81 static int map_addr(struct sk_buff
*skb
,
82 const char **dptr
, unsigned int *datalen
,
83 unsigned int matchoff
, unsigned int matchlen
,
86 enum ip_conntrack_info ctinfo
;
87 struct nf_conn
*ct __maybe_unused
= nf_ct_get(skb
, &ctinfo
);
88 enum ip_conntrack_dir dir
= CTINFO2DIR(ctinfo
);
92 if ((matchlen
== map
->addr
[dir
].srciplen
||
93 matchlen
== map
->addr
[dir
].srclen
) &&
94 strncmp(*dptr
+ matchoff
, map
->addr
[dir
].src
, matchlen
) == 0) {
95 addr
= map
->addr
[!dir
].dst
;
96 addrlen
= map
->addr
[!dir
].dstlen
;
97 } else if ((matchlen
== map
->addr
[dir
].dstiplen
||
98 matchlen
== map
->addr
[dir
].dstlen
) &&
99 strncmp(*dptr
+ matchoff
, map
->addr
[dir
].dst
, matchlen
) == 0) {
100 addr
= map
->addr
[!dir
].src
;
101 addrlen
= map
->addr
[!dir
].srclen
;
105 return mangle_packet(skb
, dptr
, datalen
, matchoff
, matchlen
,
109 static int map_sip_addr(struct sk_buff
*skb
,
110 const char **dptr
, unsigned int *datalen
,
111 enum sip_header_types type
, struct addr_map
*map
)
113 enum ip_conntrack_info ctinfo
;
114 struct nf_conn
*ct
= nf_ct_get(skb
, &ctinfo
);
115 unsigned int matchlen
, matchoff
;
117 if (ct_sip_get_header(ct
, *dptr
, 0, *datalen
, type
,
118 &matchoff
, &matchlen
) <= 0)
120 return map_addr(skb
, dptr
, datalen
, matchoff
, matchlen
, map
);
123 static unsigned int ip_nat_sip(struct sk_buff
*skb
,
124 const char **dptr
, unsigned int *datalen
)
126 enum ip_conntrack_info ctinfo
;
127 struct nf_conn
*ct
= nf_ct_get(skb
, &ctinfo
);
129 unsigned int matchoff
, matchlen
;
131 if (*datalen
< strlen("SIP/2.0"))
134 addr_map_init(ct
, &map
);
136 /* Basic rules: requests and responses. */
137 if (strnicmp(*dptr
, "SIP/2.0", strlen("SIP/2.0")) != 0) {
138 if (ct_sip_parse_request(ct
, *dptr
, *datalen
,
139 &matchoff
, &matchlen
) > 0 &&
140 !map_addr(skb
, dptr
, datalen
, matchoff
, matchlen
, &map
))
144 if (!map_sip_addr(skb
, dptr
, datalen
, SIP_HDR_FROM
, &map
) ||
145 !map_sip_addr(skb
, dptr
, datalen
, SIP_HDR_TO
, &map
) ||
146 !map_sip_addr(skb
, dptr
, datalen
, SIP_HDR_VIA
, &map
) ||
147 !map_sip_addr(skb
, dptr
, datalen
, SIP_HDR_CONTACT
, &map
))
152 static int mangle_content_len(struct sk_buff
*skb
,
153 const char **dptr
, unsigned int *datalen
)
155 enum ip_conntrack_info ctinfo
;
156 struct nf_conn
*ct
= nf_ct_get(skb
, &ctinfo
);
157 unsigned int matchoff
, matchlen
;
158 char buffer
[sizeof("65536")];
161 /* Get actual SDP length */
162 if (ct_sip_get_sdp_header(ct
, *dptr
, 0, *datalen
,
163 SDP_HDR_VERSION
, SDP_HDR_UNSPEC
,
164 &matchoff
, &matchlen
) <= 0)
166 c_len
= *datalen
- matchoff
+ strlen("v=");
168 /* Now, update SDP length */
169 if (ct_sip_get_header(ct
, *dptr
, 0, *datalen
, SIP_HDR_CONTENT_LENGTH
,
170 &matchoff
, &matchlen
) <= 0)
173 buflen
= sprintf(buffer
, "%u", c_len
);
174 return mangle_packet(skb
, dptr
, datalen
, matchoff
, matchlen
,
178 static unsigned mangle_sdp_packet(struct sk_buff
*skb
,
179 const char **dptr
, unsigned int *datalen
,
180 enum sdp_header_types type
,
181 char *buffer
, int buflen
)
183 enum ip_conntrack_info ctinfo
;
184 struct nf_conn
*ct
= nf_ct_get(skb
, &ctinfo
);
185 unsigned int matchlen
, matchoff
;
187 if (ct_sip_get_sdp_header(ct
, *dptr
, 0, *datalen
, type
, SDP_HDR_UNSPEC
,
188 &matchoff
, &matchlen
) <= 0)
190 return mangle_packet(skb
, dptr
, datalen
, matchoff
, matchlen
,
194 static unsigned int mangle_sdp(struct sk_buff
*skb
,
195 enum ip_conntrack_info ctinfo
,
197 __be32 newip
, u_int16_t port
,
198 const char **dptr
, unsigned int *datalen
)
200 char buffer
[sizeof("nnn.nnn.nnn.nnn")];
201 unsigned int bufflen
;
203 /* Mangle owner and contact info. */
204 bufflen
= sprintf(buffer
, "%u.%u.%u.%u", NIPQUAD(newip
));
205 if (!mangle_sdp_packet(skb
, dptr
, datalen
, SDP_HDR_OWNER_IP4
,
209 if (!mangle_sdp_packet(skb
, dptr
, datalen
, SDP_HDR_CONNECTION_IP4
,
213 /* Mangle media port. */
214 bufflen
= sprintf(buffer
, "%u", port
);
215 if (!mangle_sdp_packet(skb
, dptr
, datalen
, SDP_HDR_MEDIA
,
219 return mangle_content_len(skb
, dptr
, datalen
);
222 static void ip_nat_sdp_expect(struct nf_conn
*ct
,
223 struct nf_conntrack_expect
*exp
)
225 struct nf_nat_range range
;
227 /* This must be a fresh one. */
228 BUG_ON(ct
->status
& IPS_NAT_DONE_MASK
);
230 /* For DST manip, map port here to where it's expected. */
231 range
.flags
= (IP_NAT_RANGE_MAP_IPS
| IP_NAT_RANGE_PROTO_SPECIFIED
);
232 range
.min
= range
.max
= exp
->saved_proto
;
233 range
.min_ip
= range
.max_ip
= exp
->saved_ip
;
234 nf_nat_setup_info(ct
, &range
, IP_NAT_MANIP_DST
);
236 /* Change src to where master sends to */
237 range
.flags
= IP_NAT_RANGE_MAP_IPS
;
238 range
.min_ip
= range
.max_ip
239 = ct
->master
->tuplehash
[!exp
->dir
].tuple
.dst
.u3
.ip
;
240 nf_nat_setup_info(ct
, &range
, IP_NAT_MANIP_SRC
);
243 /* So, this packet has hit the connection tracking matching code.
244 Mangle it, and change the expectation to match the new version. */
245 static unsigned int ip_nat_sdp(struct sk_buff
*skb
,
246 const char **dptr
, unsigned int *datalen
,
247 struct nf_conntrack_expect
*exp
)
249 enum ip_conntrack_info ctinfo
;
250 struct nf_conn
*ct
= nf_ct_get(skb
, &ctinfo
);
251 enum ip_conntrack_dir dir
= CTINFO2DIR(ctinfo
);
255 /* Connection will come from reply */
256 if (ct
->tuplehash
[dir
].tuple
.src
.u3
.ip
==
257 ct
->tuplehash
[!dir
].tuple
.dst
.u3
.ip
)
258 newip
= exp
->tuple
.dst
.u3
.ip
;
260 newip
= ct
->tuplehash
[!dir
].tuple
.dst
.u3
.ip
;
262 exp
->saved_ip
= exp
->tuple
.dst
.u3
.ip
;
263 exp
->tuple
.dst
.u3
.ip
= newip
;
264 exp
->saved_proto
.udp
.port
= exp
->tuple
.dst
.u
.udp
.port
;
267 /* When you see the packet, we need to NAT it the same as the
269 exp
->expectfn
= ip_nat_sdp_expect
;
271 /* Try to get same port: if not, try to change it. */
272 for (port
= ntohs(exp
->saved_proto
.udp
.port
); port
!= 0; port
++) {
273 exp
->tuple
.dst
.u
.udp
.port
= htons(port
);
274 if (nf_ct_expect_related(exp
) == 0)
281 if (!mangle_sdp(skb
, ctinfo
, ct
, newip
, port
, dptr
, datalen
)) {
282 nf_ct_unexpect_related(exp
);
288 static void __exit
nf_nat_sip_fini(void)
290 rcu_assign_pointer(nf_nat_sip_hook
, NULL
);
291 rcu_assign_pointer(nf_nat_sdp_hook
, NULL
);
295 static int __init
nf_nat_sip_init(void)
297 BUG_ON(nf_nat_sip_hook
!= NULL
);
298 BUG_ON(nf_nat_sdp_hook
!= NULL
);
299 rcu_assign_pointer(nf_nat_sip_hook
, ip_nat_sip
);
300 rcu_assign_pointer(nf_nat_sdp_hook
, ip_nat_sdp
);
304 module_init(nf_nat_sip_init
);
305 module_exit(nf_nat_sip_fini
);