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>
14 #include <linux/udp.h>
16 #include <linux/netfilter_ipv4.h>
17 #include <linux/netfilter_ipv4/ip_nat.h>
18 #include <linux/netfilter_ipv4/ip_nat_helper.h>
19 #include <linux/netfilter_ipv4/ip_conntrack_helper.h>
20 #include <linux/netfilter_ipv4/ip_conntrack_sip.h>
22 MODULE_LICENSE("GPL");
23 MODULE_AUTHOR("Christian Hentschel <chentschel@arnet.com.ar>");
24 MODULE_DESCRIPTION("SIP NAT helper");
29 #define DEBUGP(format, args...)
32 extern struct sip_header_nfo ct_sip_hdrs
[];
34 static unsigned int mangle_sip_packet(struct sk_buff
**pskb
,
35 enum ip_conntrack_info ctinfo
,
36 struct ip_conntrack
*ct
,
37 const char **dptr
, size_t dlen
,
38 char *buffer
, int bufflen
,
39 struct sip_header_nfo
*hnfo
)
41 unsigned int matchlen
, matchoff
;
43 if (ct_sip_get_info(*dptr
, dlen
, &matchoff
, &matchlen
, hnfo
) <= 0)
46 if (!ip_nat_mangle_udp_packet(pskb
, ct
, ctinfo
,
47 matchoff
, matchlen
, buffer
, bufflen
))
50 /* We need to reload this. Thanks Patrick. */
51 *dptr
= (*pskb
)->data
+ (*pskb
)->nh
.iph
->ihl
*4 + sizeof(struct udphdr
);
55 static unsigned int ip_nat_sip(struct sk_buff
**pskb
,
56 enum ip_conntrack_info ctinfo
,
57 struct ip_conntrack
*ct
,
60 enum ip_conntrack_dir dir
= CTINFO2DIR(ctinfo
);
61 char buffer
[sizeof("nnn.nnn.nnn.nnn:nnnnn")];
62 unsigned int bufflen
, dataoff
;
66 dataoff
= (*pskb
)->nh
.iph
->ihl
*4 + sizeof(struct udphdr
);
68 ip
= ct
->tuplehash
[!dir
].tuple
.dst
.ip
;
69 port
= ct
->tuplehash
[!dir
].tuple
.dst
.u
.udp
.port
;
70 bufflen
= sprintf(buffer
, "%u.%u.%u.%u:%u", NIPQUAD(ip
), ntohs(port
));
73 if (((*pskb
)->len
- dataoff
) < (sizeof("SIP/2.0") - 1))
76 /* Basic rules: requests and responses. */
77 if (memcmp(*dptr
, "SIP/2.0", sizeof("SIP/2.0") - 1) == 0) {
80 if ((ctinfo
) < IP_CT_IS_REPLY
) {
81 mangle_sip_packet(pskb
, ctinfo
, ct
, dptr
,
82 (*pskb
)->len
- dataoff
,
84 &ct_sip_hdrs
[POS_CONTACT
]);
88 if (!mangle_sip_packet(pskb
, ctinfo
, ct
, dptr
,
89 (*pskb
)->len
- dataoff
,
90 buffer
, bufflen
, &ct_sip_hdrs
[POS_VIA
]))
93 /* This search should ignore case, but later.. */
94 aux
= ct_sip_search("CSeq:", *dptr
, sizeof("CSeq:") - 1,
95 (*pskb
)->len
- dataoff
);
99 if (!ct_sip_search("REGISTER", aux
, sizeof("REGISTER"),
100 ct_sip_lnlen(aux
, *dptr
+ (*pskb
)->len
- dataoff
)))
103 return mangle_sip_packet(pskb
, ctinfo
, ct
, dptr
,
104 (*pskb
)->len
- dataoff
,
106 &ct_sip_hdrs
[POS_CONTACT
]);
108 if ((ctinfo
) < IP_CT_IS_REPLY
) {
109 if (!mangle_sip_packet(pskb
, ctinfo
, ct
, dptr
,
110 (*pskb
)->len
- dataoff
,
111 buffer
, bufflen
, &ct_sip_hdrs
[POS_VIA
]))
114 /* Mangle Contact if exists only. - watch udp_nat_mangle()! */
115 mangle_sip_packet(pskb
, ctinfo
, ct
, dptr
, (*pskb
)->len
- dataoff
,
116 buffer
, bufflen
, &ct_sip_hdrs
[POS_CONTACT
]);
119 /* This mangle requests headers. */
120 return mangle_sip_packet(pskb
, ctinfo
, ct
, dptr
,
122 *dptr
+ (*pskb
)->len
- dataoff
),
123 buffer
, bufflen
, &ct_sip_hdrs
[POS_REQ_HEADER
]);
126 static int mangle_content_len(struct sk_buff
**pskb
,
127 enum ip_conntrack_info ctinfo
,
128 struct ip_conntrack
*ct
,
131 unsigned int dataoff
, matchoff
, matchlen
;
132 char buffer
[sizeof("65536")];
135 dataoff
= (*pskb
)->nh
.iph
->ihl
*4 + sizeof(struct udphdr
);
137 /* Get actual SDP lenght */
138 if (ct_sip_get_info(dptr
, (*pskb
)->len
- dataoff
, &matchoff
,
139 &matchlen
, &ct_sip_hdrs
[POS_SDP_HEADER
]) > 0) {
141 /* since ct_sip_get_info() give us a pointer passing 'v='
142 we need to add 2 bytes in this count. */
143 int c_len
= (*pskb
)->len
- dataoff
- matchoff
+ 2;
145 /* Now, update SDP lenght */
146 if (ct_sip_get_info(dptr
, (*pskb
)->len
- dataoff
, &matchoff
,
147 &matchlen
, &ct_sip_hdrs
[POS_CONTENT
]) > 0) {
149 bufflen
= sprintf(buffer
, "%u", c_len
);
151 return ip_nat_mangle_udp_packet(pskb
, ct
, ctinfo
,
159 static unsigned int mangle_sdp(struct sk_buff
**pskb
,
160 enum ip_conntrack_info ctinfo
,
161 struct ip_conntrack
*ct
,
162 u_int32_t newip
, u_int16_t port
,
165 char buffer
[sizeof("nnn.nnn.nnn.nnn")];
166 unsigned int dataoff
, bufflen
;
168 dataoff
= (*pskb
)->nh
.iph
->ihl
*4 + sizeof(struct udphdr
);
170 /* Mangle owner and contact info. */
171 bufflen
= sprintf(buffer
, "%u.%u.%u.%u", NIPQUAD(newip
));
172 if (!mangle_sip_packet(pskb
, ctinfo
, ct
, &dptr
, (*pskb
)->len
- dataoff
,
173 buffer
, bufflen
, &ct_sip_hdrs
[POS_OWNER
]))
176 if (!mangle_sip_packet(pskb
, ctinfo
, ct
, &dptr
, (*pskb
)->len
- dataoff
,
177 buffer
, bufflen
, &ct_sip_hdrs
[POS_CONNECTION
]))
180 /* Mangle media port. */
181 bufflen
= sprintf(buffer
, "%u", port
);
182 if (!mangle_sip_packet(pskb
, ctinfo
, ct
, &dptr
, (*pskb
)->len
- dataoff
,
183 buffer
, bufflen
, &ct_sip_hdrs
[POS_MEDIA
]))
186 return mangle_content_len(pskb
, ctinfo
, ct
, dptr
);
189 /* So, this packet has hit the connection tracking matching code.
190 Mangle it, and change the expectation to match the new version. */
191 static unsigned int ip_nat_sdp(struct sk_buff
**pskb
,
192 enum ip_conntrack_info ctinfo
,
193 struct ip_conntrack_expect
*exp
,
196 struct ip_conntrack
*ct
= exp
->master
;
197 enum ip_conntrack_dir dir
= CTINFO2DIR(ctinfo
);
201 DEBUGP("ip_nat_sdp():\n");
203 /* Connection will come from reply */
204 newip
= ct
->tuplehash
[!dir
].tuple
.dst
.ip
;
206 exp
->tuple
.dst
.ip
= newip
;
207 exp
->saved_proto
.udp
.port
= exp
->tuple
.dst
.u
.udp
.port
;
210 /* When you see the packet, we need to NAT it the same as the
212 exp
->expectfn
= ip_nat_follow_master
;
214 /* Try to get same port: if not, try to change it. */
215 for (port
= ntohs(exp
->saved_proto
.udp
.port
); port
!= 0; port
++) {
216 exp
->tuple
.dst
.u
.udp
.port
= htons(port
);
217 if (ip_conntrack_expect_related(exp
) == 0)
224 if (!mangle_sdp(pskb
, ctinfo
, ct
, newip
, port
, dptr
)) {
225 ip_conntrack_unexpect_related(exp
);
231 static void __exit
fini(void)
233 ip_nat_sip_hook
= NULL
;
234 ip_nat_sdp_hook
= NULL
;
235 /* Make sure noone calls it, meanwhile. */
239 static int __init
init(void)
241 BUG_ON(ip_nat_sip_hook
);
242 BUG_ON(ip_nat_sdp_hook
);
243 ip_nat_sip_hook
= ip_nat_sip
;
244 ip_nat_sdp_hook
= ip_nat_sdp
;