[NETFILTER]: sip conntrack: minor cleanup
[powerpc.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 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                 /* This search should ignore case, but later.. */
91                 aux = ct_sip_search("CSeq:", *dptr, sizeof("CSeq:") - 1,
92                                     (*pskb)->len - dataoff);
93                 if (!aux)
94                         return 0;
95
96                 if (!ct_sip_search("REGISTER", aux, sizeof("REGISTER"),
97                     ct_sip_lnlen(aux, *dptr + (*pskb)->len - dataoff)))
98                         return 1;
99
100                 return mangle_sip_packet(pskb, ctinfo, ct, dptr,
101                                          (*pskb)->len - dataoff,
102                                          buffer, bufflen, POS_CONTACT);
103         }
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))
108                         return 0;
109
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);
113                 return 1;
114         }
115         /* This mangle requests headers. */
116         return mangle_sip_packet(pskb, ctinfo, ct, dptr,
117                                  ct_sip_lnlen(*dptr,
118                                               *dptr + (*pskb)->len - dataoff),
119                                  buffer, bufflen, POS_REQ_HEADER);
120 }
121
122 static int mangle_content_len(struct sk_buff **pskb,
123                               enum ip_conntrack_info ctinfo,
124                               struct ip_conntrack *ct,
125                               const char *dptr)
126 {
127         unsigned int dataoff, matchoff, matchlen;
128         char buffer[sizeof("65536")];
129         int bufflen;
130
131         dataoff = (*pskb)->nh.iph->ihl*4 + sizeof(struct udphdr);
132
133         /* Get actual SDP lenght */
134         if (ct_sip_get_info(dptr, (*pskb)->len - dataoff, &matchoff,
135                             &matchlen, POS_SDP_HEADER) > 0) {
136
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;
140
141                 /* Now, update SDP lenght */
142                 if (ct_sip_get_info(dptr, (*pskb)->len - dataoff, &matchoff,
143                                     &matchlen, POS_CONTENT) > 0) {
144
145                         bufflen = sprintf(buffer, "%u", c_len);
146
147                         return ip_nat_mangle_udp_packet(pskb, ct, ctinfo,
148                                                         matchoff, matchlen,
149                                                         buffer, bufflen);
150                 }
151         }
152         return 0;
153 }
154
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,
159                                const char *dptr)
160 {
161         char buffer[sizeof("nnn.nnn.nnn.nnn")];
162         unsigned int dataoff, bufflen;
163
164         dataoff = (*pskb)->nh.iph->ihl*4 + sizeof(struct udphdr);
165
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))
170                 return 0;
171
172         if (!mangle_sip_packet(pskb, ctinfo, ct, &dptr, (*pskb)->len - dataoff,
173                                buffer, bufflen, POS_CONNECTION))
174                 return 0;
175
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))
180                 return 0;
181
182         return mangle_content_len(pskb, ctinfo, ct, dptr);
183 }
184
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,
190                                const char *dptr)
191 {
192         struct ip_conntrack *ct = exp->master;
193         enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo);
194         __be32 newip;
195         u_int16_t port;
196
197         DEBUGP("ip_nat_sdp():\n");
198
199         /* Connection will come from reply */
200         newip = ct->tuplehash[!dir].tuple.dst.ip;
201
202         exp->tuple.dst.ip = newip;
203         exp->saved_proto.udp.port = exp->tuple.dst.u.udp.port;
204         exp->dir = !dir;
205
206         /* When you see the packet, we need to NAT it the same as the
207            this one. */
208         exp->expectfn = ip_nat_follow_master;
209
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)
214                         break;
215         }
216
217         if (port == 0)
218                 return NF_DROP;
219
220         if (!mangle_sdp(pskb, ctinfo, ct, newip, port, dptr)) {
221                 ip_conntrack_unexpect_related(exp);
222                 return NF_DROP;
223         }
224         return NF_ACCEPT;
225 }
226
227 static void __exit fini(void)
228 {
229         rcu_assign_pointer(ip_nat_sip_hook, NULL);
230         rcu_assign_pointer(ip_nat_sdp_hook, NULL);
231         synchronize_rcu();
232 }
233
234 static int __init init(void)
235 {
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);
240         return 0;
241 }
242
243 module_init(init);
244 module_exit(fini);