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;