Linux kernel & device driver programming

Cross-Referenced Linux and Device Driver Code

[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ]
Version: [ 2.6.11.8 ] [ 2.6.25 ] [ 2.6.25.8 ] [ 2.6.31.13 ] Architecture: [ i386 ]
  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 }
625 
  This page was automatically generated by the LXR engine.