Merge branch 'release' of git://git.kernel.org/pub/scm/linux/kernel/git/aegl/linux-2.6
[linux-2.6] / net / ipv4 / ipvs / ip_vs_app.c
1 /*
2  * ip_vs_app.c: Application module support for IPVS
3  *
4  * Version:     $Id: ip_vs_app.c,v 1.17 2003/03/22 06:31:21 wensong Exp $
5  *
6  * Authors:     Wensong Zhang <wensong@linuxvirtualserver.org>
7  *
8  *              This program is free software; you can redistribute it and/or
9  *              modify it under the terms of the GNU General Public License
10  *              as published by the Free Software Foundation; either version
11  *              2 of the License, or (at your option) any later version.
12  *
13  * Most code here is taken from ip_masq_app.c in kernel 2.2. The difference
14  * is that ip_vs_app module handles the reverse direction (incoming requests
15  * and outgoing responses).
16  *
17  *              IP_MASQ_APP application masquerading module
18  *
19  * Author:      Juan Jose Ciarlante, <jjciarla@raiz.uncu.edu.ar>
20  *
21  */
22
23 #include <linux/module.h>
24 #include <linux/kernel.h>
25 #include <linux/skbuff.h>
26 #include <linux/in.h>
27 #include <linux/ip.h>
28 #include <linux/netfilter.h>
29 #include <net/net_namespace.h>
30 #include <net/protocol.h>
31 #include <net/tcp.h>
32 #include <asm/system.h>
33 #include <linux/stat.h>
34 #include <linux/proc_fs.h>
35 #include <linux/seq_file.h>
36 #include <linux/mutex.h>
37
38 #include <net/ip_vs.h>
39
40 EXPORT_SYMBOL(register_ip_vs_app);
41 EXPORT_SYMBOL(unregister_ip_vs_app);
42 EXPORT_SYMBOL(register_ip_vs_app_inc);
43
44 /* ipvs application list head */
45 static LIST_HEAD(ip_vs_app_list);
46 static DEFINE_MUTEX(__ip_vs_app_mutex);
47
48
49 /*
50  *      Get an ip_vs_app object
51  */
52 static inline int ip_vs_app_get(struct ip_vs_app *app)
53 {
54         return try_module_get(app->module);
55 }
56
57
58 static inline void ip_vs_app_put(struct ip_vs_app *app)
59 {
60         module_put(app->module);
61 }
62
63
64 /*
65  *      Allocate/initialize app incarnation and register it in proto apps.
66  */
67 static int
68 ip_vs_app_inc_new(struct ip_vs_app *app, __u16 proto, __u16 port)
69 {
70         struct ip_vs_protocol *pp;
71         struct ip_vs_app *inc;
72         int ret;
73
74         if (!(pp = ip_vs_proto_get(proto)))
75                 return -EPROTONOSUPPORT;
76
77         if (!pp->unregister_app)
78                 return -EOPNOTSUPP;
79
80         inc = kmemdup(app, sizeof(*inc), GFP_KERNEL);
81         if (!inc)
82                 return -ENOMEM;
83         INIT_LIST_HEAD(&inc->p_list);
84         INIT_LIST_HEAD(&inc->incs_list);
85         inc->app = app;
86         inc->port = htons(port);
87         atomic_set(&inc->usecnt, 0);
88
89         if (app->timeouts) {
90                 inc->timeout_table =
91                         ip_vs_create_timeout_table(app->timeouts,
92                                                    app->timeouts_size);
93                 if (!inc->timeout_table) {
94                         ret = -ENOMEM;
95                         goto out;
96                 }
97         }
98
99         ret = pp->register_app(inc);
100         if (ret)
101                 goto out;
102
103         list_add(&inc->a_list, &app->incs_list);
104         IP_VS_DBG(9, "%s application %s:%u registered\n",
105                   pp->name, inc->name, inc->port);
106
107         return 0;
108
109   out:
110         kfree(inc->timeout_table);
111         kfree(inc);
112         return ret;
113 }
114
115
116 /*
117  *      Release app incarnation
118  */
119 static void
120 ip_vs_app_inc_release(struct ip_vs_app *inc)
121 {
122         struct ip_vs_protocol *pp;
123
124         if (!(pp = ip_vs_proto_get(inc->protocol)))
125                 return;
126
127         if (pp->unregister_app)
128                 pp->unregister_app(inc);
129
130         IP_VS_DBG(9, "%s App %s:%u unregistered\n",
131                   pp->name, inc->name, inc->port);
132
133         list_del(&inc->a_list);
134
135         kfree(inc->timeout_table);
136         kfree(inc);
137 }
138
139
140 /*
141  *      Get reference to app inc (only called from softirq)
142  *
143  */
144 int ip_vs_app_inc_get(struct ip_vs_app *inc)
145 {
146         int result;
147
148         atomic_inc(&inc->usecnt);
149         if (unlikely((result = ip_vs_app_get(inc->app)) != 1))
150                 atomic_dec(&inc->usecnt);
151         return result;
152 }
153
154
155 /*
156  *      Put the app inc (only called from timer or net softirq)
157  */
158 void ip_vs_app_inc_put(struct ip_vs_app *inc)
159 {
160         ip_vs_app_put(inc->app);
161         atomic_dec(&inc->usecnt);
162 }
163
164
165 /*
166  *      Register an application incarnation in protocol applications
167  */
168 int
169 register_ip_vs_app_inc(struct ip_vs_app *app, __u16 proto, __u16 port)
170 {
171         int result;
172
173         mutex_lock(&__ip_vs_app_mutex);
174
175         result = ip_vs_app_inc_new(app, proto, port);
176
177         mutex_unlock(&__ip_vs_app_mutex);
178
179         return result;
180 }
181
182
183 /*
184  *      ip_vs_app registration routine
185  */
186 int register_ip_vs_app(struct ip_vs_app *app)
187 {
188         /* increase the module use count */
189         ip_vs_use_count_inc();
190
191         mutex_lock(&__ip_vs_app_mutex);
192
193         list_add(&app->a_list, &ip_vs_app_list);
194
195         mutex_unlock(&__ip_vs_app_mutex);
196
197         return 0;
198 }
199
200
201 /*
202  *      ip_vs_app unregistration routine
203  *      We are sure there are no app incarnations attached to services
204  */
205 void unregister_ip_vs_app(struct ip_vs_app *app)
206 {
207         struct ip_vs_app *inc, *nxt;
208
209         mutex_lock(&__ip_vs_app_mutex);
210
211         list_for_each_entry_safe(inc, nxt, &app->incs_list, a_list) {
212                 ip_vs_app_inc_release(inc);
213         }
214
215         list_del(&app->a_list);
216
217         mutex_unlock(&__ip_vs_app_mutex);
218
219         /* decrease the module use count */
220         ip_vs_use_count_dec();
221 }
222
223
224 /*
225  *      Bind ip_vs_conn to its ip_vs_app (called by cp constructor)
226  */
227 int ip_vs_bind_app(struct ip_vs_conn *cp, struct ip_vs_protocol *pp)
228 {
229         return pp->app_conn_bind(cp);
230 }
231
232
233 /*
234  *      Unbind cp from application incarnation (called by cp destructor)
235  */
236 void ip_vs_unbind_app(struct ip_vs_conn *cp)
237 {
238         struct ip_vs_app *inc = cp->app;
239
240         if (!inc)
241                 return;
242
243         if (inc->unbind_conn)
244                 inc->unbind_conn(inc, cp);
245         if (inc->done_conn)
246                 inc->done_conn(inc, cp);
247         ip_vs_app_inc_put(inc);
248         cp->app = NULL;
249 }
250
251
252 /*
253  *      Fixes th->seq based on ip_vs_seq info.
254  */
255 static inline void vs_fix_seq(const struct ip_vs_seq *vseq, struct tcphdr *th)
256 {
257         __u32 seq = ntohl(th->seq);
258
259         /*
260          *      Adjust seq with delta-offset for all packets after
261          *      the most recent resized pkt seq and with previous_delta offset
262          *      for all packets before most recent resized pkt seq.
263          */
264         if (vseq->delta || vseq->previous_delta) {
265                 if(after(seq, vseq->init_seq)) {
266                         th->seq = htonl(seq + vseq->delta);
267                         IP_VS_DBG(9, "vs_fix_seq(): added delta (%d) to seq\n",
268                                   vseq->delta);
269                 } else {
270                         th->seq = htonl(seq + vseq->previous_delta);
271                         IP_VS_DBG(9, "vs_fix_seq(): added previous_delta "
272                                   "(%d) to seq\n", vseq->previous_delta);
273                 }
274         }
275 }
276
277
278 /*
279  *      Fixes th->ack_seq based on ip_vs_seq info.
280  */
281 static inline void
282 vs_fix_ack_seq(const struct ip_vs_seq *vseq, struct tcphdr *th)
283 {
284         __u32 ack_seq = ntohl(th->ack_seq);
285
286         /*
287          * Adjust ack_seq with delta-offset for
288          * the packets AFTER most recent resized pkt has caused a shift
289          * for packets before most recent resized pkt, use previous_delta
290          */
291         if (vseq->delta || vseq->previous_delta) {
292                 /* since ack_seq is the number of octet that is expected
293                    to receive next, so compare it with init_seq+delta */
294                 if(after(ack_seq, vseq->init_seq+vseq->delta)) {
295                         th->ack_seq = htonl(ack_seq - vseq->delta);
296                         IP_VS_DBG(9, "vs_fix_ack_seq(): subtracted delta "
297                                   "(%d) from ack_seq\n", vseq->delta);
298
299                 } else {
300                         th->ack_seq = htonl(ack_seq - vseq->previous_delta);
301                         IP_VS_DBG(9, "vs_fix_ack_seq(): subtracted "
302                                   "previous_delta (%d) from ack_seq\n",
303                                   vseq->previous_delta);
304                 }
305         }
306 }
307
308
309 /*
310  *      Updates ip_vs_seq if pkt has been resized
311  *      Assumes already checked proto==IPPROTO_TCP and diff!=0.
312  */
313 static inline void vs_seq_update(struct ip_vs_conn *cp, struct ip_vs_seq *vseq,
314                                  unsigned flag, __u32 seq, int diff)
315 {
316         /* spinlock is to keep updating cp->flags atomic */
317         spin_lock(&cp->lock);
318         if (!(cp->flags & flag) || after(seq, vseq->init_seq)) {
319                 vseq->previous_delta = vseq->delta;
320                 vseq->delta += diff;
321                 vseq->init_seq = seq;
322                 cp->flags |= flag;
323         }
324         spin_unlock(&cp->lock);
325 }
326
327 static inline int app_tcp_pkt_out(struct ip_vs_conn *cp, struct sk_buff *skb,
328                                   struct ip_vs_app *app)
329 {
330         int diff;
331         const unsigned int tcp_offset = ip_hdrlen(skb);
332         struct tcphdr *th;
333         __u32 seq;
334
335         if (!skb_make_writable(skb, tcp_offset + sizeof(*th)))
336                 return 0;
337
338         th = (struct tcphdr *)(skb_network_header(skb) + tcp_offset);
339
340         /*
341          *      Remember seq number in case this pkt gets resized
342          */
343         seq = ntohl(th->seq);
344
345         /*
346          *      Fix seq stuff if flagged as so.
347          */
348         if (cp->flags & IP_VS_CONN_F_OUT_SEQ)
349                 vs_fix_seq(&cp->out_seq, th);
350         if (cp->flags & IP_VS_CONN_F_IN_SEQ)
351                 vs_fix_ack_seq(&cp->in_seq, th);
352
353         /*
354          *      Call private output hook function
355          */
356         if (app->pkt_out == NULL)
357                 return 1;
358
359         if (!app->pkt_out(app, cp, skb, &diff))
360                 return 0;
361
362         /*
363          *      Update ip_vs seq stuff if len has changed.
364          */
365         if (diff != 0)
366                 vs_seq_update(cp, &cp->out_seq,
367                               IP_VS_CONN_F_OUT_SEQ, seq, diff);
368
369         return 1;
370 }
371
372 /*
373  *      Output pkt hook. Will call bound ip_vs_app specific function
374  *      called by ipvs packet handler, assumes previously checked cp!=NULL
375  *      returns false if it can't handle packet (oom)
376  */
377 int ip_vs_app_pkt_out(struct ip_vs_conn *cp, struct sk_buff *skb)
378 {
379         struct ip_vs_app *app;
380
381         /*
382          *      check if application module is bound to
383          *      this ip_vs_conn.
384          */
385         if ((app = cp->app) == NULL)
386                 return 1;
387
388         /* TCP is complicated */
389         if (cp->protocol == IPPROTO_TCP)
390                 return app_tcp_pkt_out(cp, skb, app);
391
392         /*
393          *      Call private output hook function
394          */
395         if (app->pkt_out == NULL)
396                 return 1;
397
398         return app->pkt_out(app, cp, skb, NULL);
399 }
400
401
402 static inline int app_tcp_pkt_in(struct ip_vs_conn *cp, struct sk_buff *skb,
403                                  struct ip_vs_app *app)
404 {
405         int diff;
406         const unsigned int tcp_offset = ip_hdrlen(skb);
407         struct tcphdr *th;
408         __u32 seq;
409
410         if (!skb_make_writable(skb, tcp_offset + sizeof(*th)))
411                 return 0;
412
413         th = (struct tcphdr *)(skb_network_header(skb) + tcp_offset);
414
415         /*
416          *      Remember seq number in case this pkt gets resized
417          */
418         seq = ntohl(th->seq);
419
420         /*
421          *      Fix seq stuff if flagged as so.
422          */
423         if (cp->flags & IP_VS_CONN_F_IN_SEQ)
424                 vs_fix_seq(&cp->in_seq, th);
425         if (cp->flags & IP_VS_CONN_F_OUT_SEQ)
426                 vs_fix_ack_seq(&cp->out_seq, th);
427
428         /*
429          *      Call private input hook function
430          */
431         if (app->pkt_in == NULL)
432                 return 1;
433
434         if (!app->pkt_in(app, cp, skb, &diff))
435                 return 0;
436
437         /*
438          *      Update ip_vs seq stuff if len has changed.
439          */
440         if (diff != 0)
441                 vs_seq_update(cp, &cp->in_seq,
442                               IP_VS_CONN_F_IN_SEQ, seq, diff);
443
444         return 1;
445 }
446
447 /*
448  *      Input pkt hook. Will call bound ip_vs_app specific function
449  *      called by ipvs packet handler, assumes previously checked cp!=NULL.
450  *      returns false if can't handle packet (oom).
451  */
452 int ip_vs_app_pkt_in(struct ip_vs_conn *cp, struct sk_buff *skb)
453 {
454         struct ip_vs_app *app;
455
456         /*
457          *      check if application module is bound to
458          *      this ip_vs_conn.
459          */
460         if ((app = cp->app) == NULL)
461                 return 1;
462
463         /* TCP is complicated */
464         if (cp->protocol == IPPROTO_TCP)
465                 return app_tcp_pkt_in(cp, skb, app);
466
467         /*
468          *      Call private input hook function
469          */
470         if (app->pkt_in == NULL)
471                 return 1;
472
473         return app->pkt_in(app, cp, skb, NULL);
474 }
475
476
477 #ifdef CONFIG_PROC_FS
478 /*
479  *      /proc/net/ip_vs_app entry function
480  */
481
482 static struct ip_vs_app *ip_vs_app_idx(loff_t pos)
483 {
484         struct ip_vs_app *app, *inc;
485
486         list_for_each_entry(app, &ip_vs_app_list, a_list) {
487                 list_for_each_entry(inc, &app->incs_list, a_list) {
488                         if (pos-- == 0)
489                                 return inc;
490                 }
491         }
492         return NULL;
493
494 }
495
496 static void *ip_vs_app_seq_start(struct seq_file *seq, loff_t *pos)
497 {
498         mutex_lock(&__ip_vs_app_mutex);
499
500         return *pos ? ip_vs_app_idx(*pos - 1) : SEQ_START_TOKEN;
501 }
502
503 static void *ip_vs_app_seq_next(struct seq_file *seq, void *v, loff_t *pos)
504 {
505         struct ip_vs_app *inc, *app;
506         struct list_head *e;
507
508         ++*pos;
509         if (v == SEQ_START_TOKEN)
510                 return ip_vs_app_idx(0);
511
512         inc = v;
513         app = inc->app;
514
515         if ((e = inc->a_list.next) != &app->incs_list)
516                 return list_entry(e, struct ip_vs_app, a_list);
517
518         /* go on to next application */
519         for (e = app->a_list.next; e != &ip_vs_app_list; e = e->next) {
520                 app = list_entry(e, struct ip_vs_app, a_list);
521                 list_for_each_entry(inc, &app->incs_list, a_list) {
522                         return inc;
523                 }
524         }
525         return NULL;
526 }
527
528 static void ip_vs_app_seq_stop(struct seq_file *seq, void *v)
529 {
530         mutex_unlock(&__ip_vs_app_mutex);
531 }
532
533 static int ip_vs_app_seq_show(struct seq_file *seq, void *v)
534 {
535         if (v == SEQ_START_TOKEN)
536                 seq_puts(seq, "prot port    usecnt name\n");
537         else {
538                 const struct ip_vs_app *inc = v;
539
540                 seq_printf(seq, "%-3s  %-7u %-6d %-17s\n",
541                            ip_vs_proto_name(inc->protocol),
542                            ntohs(inc->port),
543                            atomic_read(&inc->usecnt),
544                            inc->name);
545         }
546         return 0;
547 }
548
549 static const struct seq_operations ip_vs_app_seq_ops = {
550         .start = ip_vs_app_seq_start,
551         .next  = ip_vs_app_seq_next,
552         .stop  = ip_vs_app_seq_stop,
553         .show  = ip_vs_app_seq_show,
554 };
555
556 static int ip_vs_app_open(struct inode *inode, struct file *file)
557 {
558         return seq_open(file, &ip_vs_app_seq_ops);
559 }
560
561 static const struct file_operations ip_vs_app_fops = {
562         .owner   = THIS_MODULE,
563         .open    = ip_vs_app_open,
564         .read    = seq_read,
565         .llseek  = seq_lseek,
566         .release = seq_release,
567 };
568 #endif
569
570
571 /*
572  *      Replace a segment of data with a new segment
573  */
574 int ip_vs_skb_replace(struct sk_buff *skb, gfp_t pri,
575                       char *o_buf, int o_len, char *n_buf, int n_len)
576 {
577         int diff;
578         int o_offset;
579         int o_left;
580
581         EnterFunction(9);
582
583         diff = n_len - o_len;
584         o_offset = o_buf - (char *)skb->data;
585         /* The length of left data after o_buf+o_len in the skb data */
586         o_left = skb->len - (o_offset + o_len);
587
588         if (diff <= 0) {
589                 memmove(o_buf + n_len, o_buf + o_len, o_left);
590                 memcpy(o_buf, n_buf, n_len);
591                 skb_trim(skb, skb->len + diff);
592         } else if (diff <= skb_tailroom(skb)) {
593                 skb_put(skb, diff);
594                 memmove(o_buf + n_len, o_buf + o_len, o_left);
595                 memcpy(o_buf, n_buf, n_len);
596         } else {
597                 if (pskb_expand_head(skb, skb_headroom(skb), diff, pri))
598                         return -ENOMEM;
599                 skb_put(skb, diff);
600                 memmove(skb->data + o_offset + n_len,
601                         skb->data + o_offset + o_len, o_left);
602                 skb_copy_to_linear_data_offset(skb, o_offset, n_buf, n_len);
603         }
604
605         /* must update the iph total length here */
606         ip_hdr(skb)->tot_len = htons(skb->len);
607
608         LeaveFunction(9);
609         return 0;
610 }
611
612
613 int ip_vs_app_init(void)
614 {
615         /* we will replace it with proc_net_ipvs_create() soon */
616         proc_net_fops_create(&init_net, "ip_vs_app", 0, &ip_vs_app_fops);
617         return 0;
618 }
619
620
621 void ip_vs_app_cleanup(void)
622 {
623         proc_net_remove(&init_net, "ip_vs_app");
624 }