[NETFILTER]: sip conntrack: do case insensitive SIP header search
[safe/jmp/linux-2.6] / 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 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)
38 {
39         unsigned int matchlen, matchoff;
40
41         if (ct_sip_get_info(*dptr, dlen, &matchoff, &matchlen, pos) <= 0)
42                 return 0;
43
44         if (!ip_nat_mangle_udp_packet(pskb, ct, ctinfo,
45                                       matchoff, matchlen, buffer, bufflen))
46                 return 0;
47
48         /* We need to reload this. Thanks Patrick. */
49         *dptr = (*pskb)->data + (*pskb)->nh.iph->ihl*4 + sizeof(struct udphdr);
50         return 1;
51 }
52
53 static unsigned int ip_nat_sip(struct sk_buff **pskb,
54                                enum ip_conntrack_info ctinfo,
55                                struct ip_conntrack *ct,
56                                const char **dptr)
57 {
58         enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo);
59         char buffer[sizeof("nnn.nnn.nnn.nnn:nnnnn")];
60         unsigned int bufflen, dataoff;
61         __be32 ip;
62         __be16 port;
63
64         dataoff = (*pskb)->nh.iph->ihl*4 + sizeof(struct udphdr);
65
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));
69
70         /* short packet ? */
71         if (((*pskb)->len - dataoff) < (sizeof("SIP/2.0") - 1))
72                 return 0;
73
74         /* Basic rules: requests and responses. */
75         if (memcmp(*dptr, "SIP/2.0", sizeof("SIP/2.0") - 1) == 0) {
76                 const char *aux;
77
78                 if ((ctinfo) < IP_CT_IS_REPLY) {
79                         mangle_sip_packet(pskb, ctinfo, ct, dptr,
80                                           (*pskb)->len - dataoff,
81                                           buffer, bufflen, POS_CONTACT);
82                         return 1;
83                 }
84
85                 if (!mangle_sip_packet(pskb, ctinfo, ct, dptr,
86                                        (*pskb)->len - dataoff,
87                                        buffer, bufflen, POS_VIA))
88                         return 0;
89
90                 aux = ct_sip_search("CSeq:", *dptr, sizeof("CSeq:") - 1,
91                                     (*pskb)->len - dataoff, 0);
92                 if (!aux)
93                         return 0;
94
95                 if (!ct_sip_search("REGISTER", aux, sizeof("REGISTER"),
96                                    ct_sip_lnlen(aux,
97                                                 *dptr + (*pskb)->len - dataoff),
98                                    1))
99                         return 1;
100
101                 return mangle_sip_packet(pskb, ctinfo, ct, dptr,
102                                          (*pskb)->len - dataoff,
103                                          buffer, bufflen, POS_CONTACT);
104         }
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))
109                         return 0;
110
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);
114                 return 1;
115         }
116         /* This mangle requests headers. */
117         return mangle_sip_packet(pskb, ctinfo, ct, dptr,
118                                  ct_sip_lnlen(*dptr,
119                                               *dptr + (*pskb)->len - dataoff),
120                                  buffer, bufflen, POS_REQ_HEADER);
121 }
122
123 static int mangle_content_len(struct sk_buff **pskb,
124                               enum ip_conntrack_info ctinfo,
125                               struct ip_conntrack *ct,
126                               const char *dptr)
127 {
128         unsigned int dataoff, matchoff, matchlen;
129         char buffer[sizeof("65536")];
130         int bufflen;
131
132         dataoff = (*pskb)->nh.iph->ihl*4 + sizeof(struct udphdr);
133
134         /* Get actual SDP lenght */
135         if (ct_sip_get_info(dptr, (*pskb)->len - dataoff, &matchoff,
136                             &matchlen, POS_SDP_HEADER) > 0) {
137
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;
141
142                 /* Now, update SDP lenght */
143                 if (ct_sip_get_info(dptr, (*pskb)->len - dataoff, &matchoff,
144                                     &matchlen, POS_CONTENT) > 0) {
145
146                         bufflen = sprintf(buffer, "%u", c_len);
147
148                         return ip_nat_mangle_udp_packet(pskb, ct, ctinfo,
149                                                         matchoff, matchlen,
150                                                         buffer, bufflen);
151                 }
152         }
153         return 0;
154 }
155
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,
160                                const char *dptr)
161 {
162         char buffer[sizeof("nnn.nnn.nnn.nnn")];
163         unsigned int dataoff, bufflen;
164
165         dataoff = (*pskb)->nh.iph->ihl*4 + sizeof(struct udphdr);
166
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))
171                 return 0;
172
173         if (!mangle_sip_packet(pskb, ctinfo, ct, &dptr, (*pskb)->len - dataoff,
174                                buffer, bufflen, POS_CONNECTION))
175                 return 0;
176
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))
181                 return 0;
182
183         return mangle_content_len(pskb, ctinfo, ct, dptr);
184 }
185
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,
191                                const char *dptr)
192 {
193         struct ip_conntrack *ct = exp->master;
194         enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo);
195         __be32 newip;
196         u_int16_t port;
197
198         DEBUGP("ip_nat_sdp():\n");
199
200         /* Connection will come from reply */
201         newip = ct->tuplehash[!dir].tuple.dst.ip;
202
203         exp->tuple.dst.ip = newip;
204         exp->saved_proto.udp.port = exp->tuple.dst.u.udp.port;
205         exp->dir = !dir;
206
207         /* When you see the packet, we need to NAT it the same as the
208            this one. */
209         exp->expectfn = ip_nat_follow_master;
210
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)
215                         break;
216         }
217
218         if (port == 0)
219                 return NF_DROP;
220
221         if (!mangle_sdp(pskb, ctinfo, ct, newip, port, dptr)) {
222                 ip_conntrack_unexpect_related(exp);
223                 return NF_DROP;
224         }
225         return NF_ACCEPT;
226 }
227
228 static void __exit fini(void)
229 {
230         rcu_assign_pointer(ip_nat_sip_hook, NULL);
231         rcu_assign_pointer(ip_nat_sdp_hook, NULL);
232         synchronize_rcu();
233 }
234
235 static int __init init(void)
236 {
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);
241         return 0;
242 }
243
244 module_init(init);
245 module_exit(fini);