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 /* This search should ignore case, but later.. */
91 aux = ct_sip_search("CSeq:", *dptr, sizeof("CSeq:") - 1,
92 (*pskb)->len - dataoff);
96 if (!ct_sip_search("REGISTER", aux, sizeof("REGISTER"),
97 ct_sip_lnlen(aux, *dptr + (*pskb)->len - dataoff)))
100 return mangle_sip_packet(pskb, ctinfo, ct, dptr,
101 (*pskb)->len - dataoff,
102 buffer, bufflen, POS_CONTACT);
104 if ((ctinfo) < IP_CT_IS_REPLY) {
105 if (!mangle_sip_packet(pskb, ctinfo, ct, dptr,
106 (*pskb)->len - dataoff,
107 buffer, bufflen, POS_VIA))
110 /* Mangle Contact if exists only. - watch udp_nat_mangle()! */
111 mangle_sip_packet(pskb, ctinfo, ct, dptr, (*pskb)->len - dataoff,
112 buffer, bufflen, POS_CONTACT);
115 /* This mangle requests headers. */
116 return mangle_sip_packet(pskb, ctinfo, ct, dptr,
118 *dptr + (*pskb)->len - dataoff),
119 buffer, bufflen, POS_REQ_HEADER);
122 static int mangle_content_len(struct sk_buff **pskb,
123 enum ip_conntrack_info ctinfo,
124 struct ip_conntrack *ct,
127 unsigned int dataoff, matchoff, matchlen;
128 char buffer[sizeof("65536")];
131 dataoff = (*pskb)->nh.iph->ihl*4 + sizeof(struct udphdr);
133 /* Get actual SDP lenght */
134 if (ct_sip_get_info(dptr, (*pskb)->len - dataoff, &matchoff,
135 &matchlen, POS_SDP_HEADER) > 0) {
137 /* since ct_sip_get_info() give us a pointer passing 'v='
138 we need to add 2 bytes in this count. */
139 int c_len = (*pskb)->len - dataoff - matchoff + 2;
141 /* Now, update SDP lenght */
142 if (ct_sip_get_info(dptr, (*pskb)->len - dataoff, &matchoff,
143 &matchlen, POS_CONTENT) > 0) {
145 bufflen = sprintf(buffer, "%u", c_len);
147 return ip_nat_mangle_udp_packet(pskb, ct, ctinfo,
155 static unsigned int mangle_sdp(struct sk_buff **pskb,
156 enum ip_conntrack_info ctinfo,
157 struct ip_conntrack *ct,
158 __be32 newip, u_int16_t port,
161 char buffer[sizeof("nnn.nnn.nnn.nnn")];
162 unsigned int dataoff, bufflen;
164 dataoff = (*pskb)->nh.iph->ihl*4 + sizeof(struct udphdr);
166 /* Mangle owner and contact info. */
167 bufflen = sprintf(buffer, "%u.%u.%u.%u", NIPQUAD(newip));
168 if (!mangle_sip_packet(pskb, ctinfo, ct, &dptr, (*pskb)->len - dataoff,
169 buffer, bufflen, POS_OWNER))
172 if (!mangle_sip_packet(pskb, ctinfo, ct, &dptr, (*pskb)->len - dataoff,
173 buffer, bufflen, POS_CONNECTION))
176 /* Mangle media port. */
177 bufflen = sprintf(buffer, "%u", port);
178 if (!mangle_sip_packet(pskb, ctinfo, ct, &dptr, (*pskb)->len - dataoff,
179 buffer, bufflen, POS_MEDIA))
182 return mangle_content_len(pskb, ctinfo, ct, dptr);
185 /* So, this packet has hit the connection tracking matching code.
186 Mangle it, and change the expectation to match the new version. */
187 static unsigned int ip_nat_sdp(struct sk_buff **pskb,
188 enum ip_conntrack_info ctinfo,
189 struct ip_conntrack_expect *exp,
192 struct ip_conntrack *ct = exp->master;
193 enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo);
197 DEBUGP("ip_nat_sdp():\n");
199 /* Connection will come from reply */
200 newip = ct->tuplehash[!dir].tuple.dst.ip;
202 exp->tuple.dst.ip = newip;
203 exp->saved_proto.udp.port = exp->tuple.dst.u.udp.port;
206 /* When you see the packet, we need to NAT it the same as the
208 exp->expectfn = ip_nat_follow_master;
210 /* Try to get same port: if not, try to change it. */
211 for (port = ntohs(exp->saved_proto.udp.port); port != 0; port++) {
212 exp->tuple.dst.u.udp.port = htons(port);
213 if (ip_conntrack_expect_related(exp) == 0)
220 if (!mangle_sdp(pskb, ctinfo, ct, newip, port, dptr)) {
221 ip_conntrack_unexpect_related(exp);
227 static void __exit fini(void)
229 rcu_assign_pointer(ip_nat_sip_hook, NULL);
230 rcu_assign_pointer(ip_nat_sdp_hook, NULL);
234 static int __init init(void)
236 BUG_ON(rcu_dereference(ip_nat_sip_hook));
237 BUG_ON(rcu_dereference(ip_nat_sdp_hook));
238 rcu_assign_pointer(ip_nat_sip_hook, ip_nat_sip);
239 rcu_assign_pointer(ip_nat_sdp_hook, ip_nat_sdp);