Merge branch 'master' of git://oak/home/sfr/kernels/iseries/work
[deliverable/linux.git] / net / ipv4 / netfilter / ip_nat_sip.c
1 /* SIP extension for UDP NAT alteration.
2 *
3 * (C) 2005 by Christian Hentschel <chentschel@arnet.com.ar>
4 * based on RR's ip_nat_ftp.c and other modules.
5 *
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.
9 */
10
11 #include <linux/module.h>
12 #include <linux/skbuff.h>
13 #include <linux/ip.h>
14 #include <linux/udp.h>
15
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>
21
22 MODULE_LICENSE("GPL");
23 MODULE_AUTHOR("Christian Hentschel <chentschel@arnet.com.ar>");
24 MODULE_DESCRIPTION("SIP NAT helper");
25
26 #if 0
27 #define DEBUGP printk
28 #else
29 #define DEBUGP(format, args...)
30 #endif
31
32 extern struct sip_header_nfo ct_sip_hdrs[];
33
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)
40 {
41 unsigned int matchlen, matchoff;
42
43 if (ct_sip_get_info(*dptr, dlen, &matchoff, &matchlen, hnfo) <= 0)
44 return 0;
45
46 if (!ip_nat_mangle_udp_packet(pskb, ct, ctinfo,
47 matchoff, matchlen, buffer, bufflen))
48 return 0;
49
50 /* We need to reload this. Thanks Patrick. */
51 *dptr = (*pskb)->data + (*pskb)->nh.iph->ihl*4 + sizeof(struct udphdr);
52 return 1;
53 }
54
55 static unsigned int ip_nat_sip(struct sk_buff **pskb,
56 enum ip_conntrack_info ctinfo,
57 struct ip_conntrack *ct,
58 const char **dptr)
59 {
60 enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo);
61 char buffer[sizeof("nnn.nnn.nnn.nnn:nnnnn")];
62 unsigned int bufflen, dataoff;
63 __be32 ip;
64 __be16 port;
65
66 dataoff = (*pskb)->nh.iph->ihl*4 + sizeof(struct udphdr);
67
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));
71
72 /* short packet ? */
73 if (((*pskb)->len - dataoff) < (sizeof("SIP/2.0") - 1))
74 return 0;
75
76 /* Basic rules: requests and responses. */
77 if (memcmp(*dptr, "SIP/2.0", sizeof("SIP/2.0") - 1) == 0) {
78 const char *aux;
79
80 if ((ctinfo) < IP_CT_IS_REPLY) {
81 mangle_sip_packet(pskb, ctinfo, ct, dptr,
82 (*pskb)->len - dataoff,
83 buffer, bufflen,
84 &ct_sip_hdrs[POS_CONTACT]);
85 return 1;
86 }
87
88 if (!mangle_sip_packet(pskb, ctinfo, ct, dptr,
89 (*pskb)->len - dataoff,
90 buffer, bufflen, &ct_sip_hdrs[POS_VIA]))
91 return 0;
92
93 /* This search should ignore case, but later.. */
94 aux = ct_sip_search("CSeq:", *dptr, sizeof("CSeq:") - 1,
95 (*pskb)->len - dataoff);
96 if (!aux)
97 return 0;
98
99 if (!ct_sip_search("REGISTER", aux, sizeof("REGISTER"),
100 ct_sip_lnlen(aux, *dptr + (*pskb)->len - dataoff)))
101 return 1;
102
103 return mangle_sip_packet(pskb, ctinfo, ct, dptr,
104 (*pskb)->len - dataoff,
105 buffer, bufflen,
106 &ct_sip_hdrs[POS_CONTACT]);
107 }
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]))
112 return 0;
113
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]);
117 return 1;
118 }
119 /* This mangle requests headers. */
120 return mangle_sip_packet(pskb, ctinfo, ct, dptr,
121 ct_sip_lnlen(*dptr,
122 *dptr + (*pskb)->len - dataoff),
123 buffer, bufflen, &ct_sip_hdrs[POS_REQ_HEADER]);
124 }
125
126 static int mangle_content_len(struct sk_buff **pskb,
127 enum ip_conntrack_info ctinfo,
128 struct ip_conntrack *ct,
129 const char *dptr)
130 {
131 unsigned int dataoff, matchoff, matchlen;
132 char buffer[sizeof("65536")];
133 int bufflen;
134
135 dataoff = (*pskb)->nh.iph->ihl*4 + sizeof(struct udphdr);
136
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) {
140
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;
144
145 /* Now, update SDP lenght */
146 if (ct_sip_get_info(dptr, (*pskb)->len - dataoff, &matchoff,
147 &matchlen, &ct_sip_hdrs[POS_CONTENT]) > 0) {
148
149 bufflen = sprintf(buffer, "%u", c_len);
150
151 return ip_nat_mangle_udp_packet(pskb, ct, ctinfo,
152 matchoff, matchlen,
153 buffer, bufflen);
154 }
155 }
156 return 0;
157 }
158
159 static unsigned int mangle_sdp(struct sk_buff **pskb,
160 enum ip_conntrack_info ctinfo,
161 struct ip_conntrack *ct,
162 __be32 newip, u_int16_t port,
163 const char *dptr)
164 {
165 char buffer[sizeof("nnn.nnn.nnn.nnn")];
166 unsigned int dataoff, bufflen;
167
168 dataoff = (*pskb)->nh.iph->ihl*4 + sizeof(struct udphdr);
169
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]))
174 return 0;
175
176 if (!mangle_sip_packet(pskb, ctinfo, ct, &dptr, (*pskb)->len - dataoff,
177 buffer, bufflen, &ct_sip_hdrs[POS_CONNECTION]))
178 return 0;
179
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]))
184 return 0;
185
186 return mangle_content_len(pskb, ctinfo, ct, dptr);
187 }
188
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,
194 const char *dptr)
195 {
196 struct ip_conntrack *ct = exp->master;
197 enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo);
198 __be32 newip;
199 u_int16_t port;
200
201 DEBUGP("ip_nat_sdp():\n");
202
203 /* Connection will come from reply */
204 newip = ct->tuplehash[!dir].tuple.dst.ip;
205
206 exp->tuple.dst.ip = newip;
207 exp->saved_proto.udp.port = exp->tuple.dst.u.udp.port;
208 exp->dir = !dir;
209
210 /* When you see the packet, we need to NAT it the same as the
211 this one. */
212 exp->expectfn = ip_nat_follow_master;
213
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)
218 break;
219 }
220
221 if (port == 0)
222 return NF_DROP;
223
224 if (!mangle_sdp(pskb, ctinfo, ct, newip, port, dptr)) {
225 ip_conntrack_unexpect_related(exp);
226 return NF_DROP;
227 }
228 return NF_ACCEPT;
229 }
230
231 static void __exit fini(void)
232 {
233 ip_nat_sip_hook = NULL;
234 ip_nat_sdp_hook = NULL;
235 /* Make sure noone calls it, meanwhile. */
236 synchronize_net();
237 }
238
239 static int __init init(void)
240 {
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;
245 return 0;
246 }
247
248 module_init(init);
249 module_exit(fini);
This page took 0.052928 seconds and 6 git commands to generate.