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 static unsigned int mangle_sip_packet(struct sk_buff **pskb,
33 enum ip_conntrack_info ctinfo,
34 struct ip_conntrack *ct,
35 const char **dptr, size_t dlen,
36 char *buffer, int bufflen,
37 enum sip_header_pos pos)
39 unsigned int matchlen, matchoff;
41 if (ct_sip_get_info(*dptr, dlen, &matchoff, &matchlen, pos) <= 0)
44 if (!ip_nat_mangle_udp_packet(pskb, ct, ctinfo,
45 matchoff, matchlen, buffer, bufflen))
48 /* We need to reload this. Thanks Patrick. */
49 *dptr = (*pskb)->data + (*pskb)->nh.iph->ihl*4 + sizeof(struct udphdr);
53 static unsigned int ip_nat_sip(struct sk_buff **pskb,
54 enum ip_conntrack_info ctinfo,
55 struct ip_conntrack *ct,
58 enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo);
59 char buffer[sizeof("nnn.nnn.nnn.nnn:nnnnn")];
60 unsigned int bufflen, dataoff;
64 dataoff = (*pskb)->nh.iph->ihl*4 + sizeof(struct udphdr);
66 ip = ct->tuplehash[!dir].tuple.dst.ip;
67 port = ct->tuplehash[!dir].tuple.dst.u.udp.port;
68 bufflen = sprintf(buffer, "%u.%u.%u.%u:%u", NIPQUAD(ip), ntohs(port));
71 if (((*pskb)->len - dataoff) < (sizeof("SIP/2.0") - 1))
74 /* Basic rules: requests and responses. */
75 if (memcmp(*dptr, "SIP/2.0", sizeof("SIP/2.0") - 1) == 0) {
78 if ((ctinfo) < IP_CT_IS_REPLY) {
79 mangle_sip_packet(pskb, ctinfo, ct, dptr,
80 (*pskb)->len - dataoff,
81 buffer, bufflen, POS_CONTACT);
85 if (!mangle_sip_packet(pskb, ctinfo, ct, dptr,
86 (*pskb)->len - dataoff,
87 buffer, bufflen, POS_VIA))
90 aux = ct_sip_search("CSeq:", *dptr, sizeof("CSeq:") - 1,
91 (*pskb)->len - dataoff, 0);
95 if (!ct_sip_search("REGISTER", aux, sizeof("REGISTER"),
97 *dptr + (*pskb)->len - dataoff),
101 return mangle_sip_packet(pskb, ctinfo, ct, dptr,
102 (*pskb)->len - dataoff,
103 buffer, bufflen, POS_CONTACT);
105 if ((ctinfo) < IP_CT_IS_REPLY) {
106 if (!mangle_sip_packet(pskb, ctinfo, ct, dptr,
107 (*pskb)->len - dataoff,
108 buffer, bufflen, POS_VIA))
111 /* Mangle Contact if exists only. - watch udp_nat_mangle()! */
112 mangle_sip_packet(pskb, ctinfo, ct, dptr, (*pskb)->len - dataoff,
113 buffer, bufflen, POS_CONTACT);
116 /* This mangle requests headers. */
117 return mangle_sip_packet(pskb, ctinfo, ct, dptr,
119 *dptr + (*pskb)->len - dataoff),
120 buffer, bufflen, POS_REQ_HEADER);
123 static int mangle_content_len(struct sk_buff **pskb,
124 enum ip_conntrack_info ctinfo,
125 struct ip_conntrack *ct,
128 unsigned int dataoff, matchoff, matchlen;
129 char buffer[sizeof("65536")];
132 dataoff = (*pskb)->nh.iph->ihl*4 + sizeof(struct udphdr);
134 /* Get actual SDP lenght */
135 if (ct_sip_get_info(dptr, (*pskb)->len - dataoff, &matchoff,
136 &matchlen, POS_SDP_HEADER) > 0) {
138 /* since ct_sip_get_info() give us a pointer passing 'v='
139 we need to add 2 bytes in this count. */
140 int c_len = (*pskb)->len - dataoff - matchoff + 2;
142 /* Now, update SDP lenght */
143 if (ct_sip_get_info(dptr, (*pskb)->len - dataoff, &matchoff,
144 &matchlen, POS_CONTENT) > 0) {
146 bufflen = sprintf(buffer, "%u", c_len);
148 return ip_nat_mangle_udp_packet(pskb, ct, ctinfo,
156 static unsigned int mangle_sdp(struct sk_buff **pskb,
157 enum ip_conntrack_info ctinfo,
158 struct ip_conntrack *ct,
159 __be32 newip, u_int16_t port,
162 char buffer[sizeof("nnn.nnn.nnn.nnn")];
163 unsigned int dataoff, bufflen;
165 dataoff = (*pskb)->nh.iph->ihl*4 + sizeof(struct udphdr);
167 /* Mangle owner and contact info. */
168 bufflen = sprintf(buffer, "%u.%u.%u.%u", NIPQUAD(newip));
169 if (!mangle_sip_packet(pskb, ctinfo, ct, &dptr, (*pskb)->len - dataoff,
170 buffer, bufflen, POS_OWNER))
173 if (!mangle_sip_packet(pskb, ctinfo, ct, &dptr, (*pskb)->len - dataoff,
174 buffer, bufflen, POS_CONNECTION))
177 /* Mangle media port. */
178 bufflen = sprintf(buffer, "%u", port);
179 if (!mangle_sip_packet(pskb, ctinfo, ct, &dptr, (*pskb)->len - dataoff,
180 buffer, bufflen, POS_MEDIA))
183 return mangle_content_len(pskb, ctinfo, ct, dptr);
186 /* So, this packet has hit the connection tracking matching code.
187 Mangle it, and change the expectation to match the new version. */
188 static unsigned int ip_nat_sdp(struct sk_buff **pskb,
189 enum ip_conntrack_info ctinfo,
190 struct ip_conntrack_expect *exp,
193 struct ip_conntrack *ct = exp->master;
194 enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo);
198 DEBUGP("ip_nat_sdp():\n");
200 /* Connection will come from reply */
201 newip = ct->tuplehash[!dir].tuple.dst.ip;
203 exp->tuple.dst.ip = newip;
204 exp->saved_proto.udp.port = exp->tuple.dst.u.udp.port;
207 /* When you see the packet, we need to NAT it the same as the
209 exp->expectfn = ip_nat_follow_master;
211 /* Try to get same port: if not, try to change it. */
212 for (port = ntohs(exp->saved_proto.udp.port); port != 0; port++) {
213 exp->tuple.dst.u.udp.port = htons(port);
214 if (ip_conntrack_expect_related(exp) == 0)
221 if (!mangle_sdp(pskb, ctinfo, ct, newip, port, dptr)) {
222 ip_conntrack_unexpect_related(exp);
228 static void __exit fini(void)
230 rcu_assign_pointer(ip_nat_sip_hook, NULL);
231 rcu_assign_pointer(ip_nat_sdp_hook, NULL);
235 static int __init init(void)
237 BUG_ON(rcu_dereference(ip_nat_sip_hook));
238 BUG_ON(rcu_dereference(ip_nat_sdp_hook));
239 rcu_assign_pointer(ip_nat_sip_hook, ip_nat_sip);
240 rcu_assign_pointer(ip_nat_sdp_hook, ip_nat_sdp);