1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18#include <linux/config.h>
19#include <linux/module.h>
20#include <linux/types.h>
21#include <linux/kernel.h>
22#include <linux/errno.h>
23#include <linux/list.h>
24#include <net/ip.h>
25#include <linux/ip_fw.h>
26#include <linux/ip_masq.h>
27#include <net/ip_masq.h>
28#include <net/ip_masq_mod.h>
29#include <linux/proc_fs.h>
30#include <linux/init.h>
31#include <asm/softirq.h>
32#include <asm/spinlock.h>
33#include <asm/atomic.h>
34
35static struct ip_masq_mod *mmod_self = NULL;
36#ifdef CONFIG_IP_MASQ_DEBUG
37static int debug=0;
38MODULE_PARM(debug, "i");
39#endif
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55struct ip_masq_mfw_host {
56 struct list_head list;
57 __u32 addr;
58 __u16 port;
59 __u16 pad0;
60 __u32 fwmark;
61 int pref;
62 atomic_t pref_cnt;
63};
64
65#define IP_MASQ_MFW_HSIZE 16
66
67
68
69
70
71struct ip_masq_mfw {
72 struct ip_masq_mfw *next;
73 __u32 fwmark;
74 struct list_head hosts;
75 atomic_t nhosts;
76#ifdef __SMP__
77 rwlock_t lock;
78#endif
79};
80
81
82static struct semaphore mfw_sema = MUTEX;
83#ifdef __SMP__
84static rwlock_t mfw_lock = RW_LOCK_UNLOCKED;
85#endif
86
87static struct ip_masq_mfw *ip_masq_mfw_table[IP_MASQ_MFW_HSIZE];
88
89static __inline__ int mfw_hash_val(int fwmark)
90{
91 return fwmark & 0x0f;
92}
93
94
95
96
97
98
99static struct ip_masq_mfw *__mfw_get(int fwmark)
100{
101 struct ip_masq_mfw* mfw;
102 int hash = mfw_hash_val(fwmark);
103
104 for (mfw=ip_masq_mfw_table[hash];mfw;mfw=mfw->next) {
105 if (mfw->fwmark==fwmark) {
106 goto out;
107 }
108 }
109out:
110 return mfw;
111}
112
113
114
115
116
117
118
119static int __mfw_add(struct ip_masq_mfw *mfw)
120{
121 int fwmark = mfw->fwmark;
122 int hash = mfw_hash_val(fwmark);
123
124 mfw->next = ip_masq_mfw_table[hash];
125 ip_masq_mfw_table[hash] = mfw;
126 ip_masq_mod_inc_nent(mmod_self);
127
128 return 0;
129}
130
131
132
133
134
135static struct ip_masq_mfw * mfw_new(int fwmark)
136{
137 struct ip_masq_mfw *mfw;
138
139 mfw = kmalloc(sizeof(*mfw), GFP_KERNEL);
140 if (mfw == NULL)
141 goto out;
142
143 MOD_INC_USE_COUNT;
144 memset(mfw, 0, sizeof(*mfw));
145 mfw->fwmark = fwmark;
146#ifdef __SMP__
147 mfw->lock = (rwlock_t) RW_LOCK_UNLOCKED;
148#endif
149
150 INIT_LIST_HEAD(&mfw->hosts);
151out:
152 return mfw;
153}
154
155static void mfw_host_to_user(struct ip_masq_mfw_host *h, struct ip_mfw_user *mu)
156{
157 mu->raddr = h->addr;
158 mu->rport = h->port;
159 mu->fwmark = h->fwmark;
160 mu->pref = h->pref;
161}
162
163
164
165
166static struct ip_masq_mfw_host * mfw_host_new(struct ip_mfw_user *mu)
167{
168 struct ip_masq_mfw_host * mfw_host;
169 mfw_host = kmalloc(sizeof (*mfw_host), GFP_KERNEL);
170 if (!mfw_host)
171 return NULL;
172
173 MOD_INC_USE_COUNT;
174 memset(mfw_host, 0, sizeof(*mfw_host));
175 mfw_host->addr = mu->raddr;
176 mfw_host->port = mu->rport;
177 mfw_host->fwmark = mu->fwmark;
178 mfw_host->pref = mu->pref;
179 atomic_set(&mfw_host->pref_cnt, mu->pref);
180
181 return mfw_host;
182}
183
184
185
186
187
188static int mfw_addhost(struct ip_masq_mfw *mfw, struct ip_mfw_user *mu, int attail)
189{
190 struct ip_masq_mfw_host *mfw_host;
191
192 mfw_host = mfw_host_new(mu);
193 if (!mfw_host)
194 return -ENOMEM;
195
196 write_lock_bh(&mfw->lock);
197 list_add(&mfw_host->list, attail? mfw->hosts.prev : &mfw->hosts);
198 atomic_inc(&mfw->nhosts);
199 write_unlock_bh(&mfw->lock);
200
201 return 0;
202}
203
204
205
206
207
208
209static int mfw_delhost(struct ip_masq_mfw *mfw, struct ip_mfw_user *mu)
210{
211
212 struct list_head *l,*e;
213 struct ip_masq_mfw_host *h;
214 int n_del = 0;
215 l = &mfw->hosts;
216
217 write_lock_bh(&mfw->lock);
218 for (e=l->next; e!=l; e=e->next)
219 {
220 h = list_entry(e, struct ip_masq_mfw_host, list);
221 if ((!mu->raddr || h->addr == mu->raddr) &&
222 (!mu->rport || h->port == mu->rport)) {
223
224 atomic_dec(&mfw->nhosts);
225 list_del(&h->list);
226 kfree_s(h, sizeof(*h));
227 MOD_DEC_USE_COUNT;
228 n_del++;
229 }
230
231 }
232 write_unlock_bh(&mfw->lock);
233 return n_del? 0 : -ESRCH;
234}
235
236
237
238
239
240
241
242static int __mfw_edithost(struct ip_masq_mfw *mfw, struct ip_mfw_user *mu)
243{
244
245 struct list_head *l,*e;
246 struct ip_masq_mfw_host *h;
247 int n_edit = 0;
248 l = &mfw->hosts;
249
250 for (e=l->next; e!=l; e=e->next)
251 {
252 h = list_entry(e, struct ip_masq_mfw_host, list);
253 if ((!mu->raddr || h->addr == mu->raddr) &&
254 (!mu->rport || h->port == mu->rport)) {
255
256 h->pref = mu->pref;
257 atomic_set(&h->pref_cnt, mu->pref);
258 n_edit++;
259 }
260
261 }
262 return n_edit? 0 : -ESRCH;
263}
264
265
266
267
268
269static void mfw_destroy(struct ip_masq_mfw *mfw)
270{
271 kfree_s(mfw, sizeof(*mfw));
272 MOD_DEC_USE_COUNT;
273}
274
275
276
277
278
279
280static int __mfw_del(struct ip_masq_mfw *mfw)
281{
282 struct ip_masq_mfw **mfw_p;
283 int ret = -EINVAL;
284
285
286 for(mfw_p=&ip_masq_mfw_table[mfw_hash_val(mfw->fwmark)];
287 *mfw_p;
288 mfw_p = &((*mfw_p)->next))
289 {
290 if (mfw==(*mfw_p)) {
291 *mfw_p = mfw->next;
292 ip_masq_mod_dec_nent(mmod_self);
293 ret = 0;
294 goto out;
295 }
296 }
297out:
298 return ret;
299}
300
301
302
303
304
305
306
307
308static struct ip_masq_mfw_host * __mfw_sched(struct ip_masq_mfw *mfw, int force)
309{
310 struct ip_masq_mfw_host *h = NULL;
311
312 if (atomic_read(&mfw->nhosts) == 0)
313 goto out;
314
315
316
317
318
319
320
321
322
323
324 h = list_entry(mfw->hosts.next, struct ip_masq_mfw_host, list);
325
326 if (atomic_read(&mfw->nhosts) <= 1)
327 goto out;
328
329 if ((h->pref && atomic_dec_and_test(&h->pref_cnt)) || force) {
330 atomic_set(&h->pref_cnt, h->pref);
331 list_del(&h->list);
332 list_add(&h->list, mfw->hosts.prev);
333 }
334out:
335 return h;
336}
337
338
339
340
341
342static struct ip_masq_mfw_host * mfw_lookup(int fwmark)
343{
344 struct ip_masq_mfw *mfw;
345 struct ip_masq_mfw_host *h = NULL;
346
347 read_lock(&mfw_lock);
348 mfw = __mfw_get(fwmark);
349
350 if (mfw) {
351 write_lock(&mfw->lock);
352 h = __mfw_sched(mfw, 0);
353 write_unlock(&mfw->lock);
354 }
355
356 read_unlock(&mfw_lock);
357 return h;
358}
359
360#ifdef CONFIG_PROC_FS
361static int mfw_procinfo(char *buffer, char **start, off_t offset,
362 int length, int dummy)
363{
364 struct ip_masq_mfw *mfw;
365 struct ip_masq_mfw_host *h;
366 struct list_head *l,*e;
367 off_t pos=0, begin;
368 char temp[129];
369 int idx = 0;
370 int len=0;
371
372 MOD_INC_USE_COUNT;
373
374 IP_MASQ_DEBUG(1-debug, "Entered mfw_info\n");
375
376 if (offset < 64)
377 {
378 sprintf(temp, "FwMark > RAddr RPort PrCnt Pref");
379 len = sprintf(buffer, "%-63s\n", temp);
380 }
381 pos = 64;
382
383 for(idx = 0; idx < IP_MASQ_MFW_HSIZE; idx++)
384 {
385 read_lock(&mfw_lock);
386 for(mfw = ip_masq_mfw_table[idx]; mfw ; mfw = mfw->next)
387 {
388 read_lock_bh(&mfw->lock);
389 l=&mfw->hosts;
390
391 for(e=l->next;l!=e;e=e->next) {
392 h = list_entry(e, struct ip_masq_mfw_host, list);
393 pos += 64;
394 if (pos <= offset) {
395 len = 0;
396 continue;
397 }
398
399 sprintf(temp,"0x%x > %08lX %5u %5d %5d",
400 h->fwmark,
401 ntohl(h->addr), ntohs(h->port),
402 atomic_read(&h->pref_cnt), h->pref);
403 len += sprintf(buffer+len, "%-63s\n", temp);
404
405 if(len >= length) {
406 read_unlock_bh(&mfw->lock);
407 read_unlock(&mfw_lock);
408 goto done;
409 }
410 }
411 read_unlock_bh(&mfw->lock);
412 }
413 read_unlock(&mfw_lock);
414 }
415
416done:
417
418 if (len) {
419 begin = len - (pos - offset);
420 *start = buffer + begin;
421 len -= begin;
422 }
423 if(len>length)
424 len = length;
425 MOD_DEC_USE_COUNT;
426 return len;
427}
428static struct proc_dir_entry mfw_proc_entry = {
429
430 0, 3, "mfw",
431 S_IFREG | S_IRUGO, 1, 0, 0,
432 0, &proc_net_inode_operations,
433 mfw_procinfo
434};
435
436#define proc_ent &mfw_proc_entry
437#else
438
439#define proc_ent NULL
440#endif
441
442
443static void mfw_flush(void)
444{
445 struct ip_masq_mfw *mfw, *local_table[IP_MASQ_MFW_HSIZE];
446 struct ip_masq_mfw_host *h;
447 struct ip_masq_mfw *mfw_next;
448 int idx;
449 struct list_head *l,*e;
450
451 write_lock_bh(&mfw_lock);
452 memcpy(local_table, ip_masq_mfw_table, sizeof ip_masq_mfw_table);
453 memset(ip_masq_mfw_table, 0, sizeof ip_masq_mfw_table);
454 write_unlock_bh(&mfw_lock);
455
456
457
458
459 for(idx=0;idx<IP_MASQ_MFW_HSIZE;idx++) {
460
461
462
463
464 for(mfw=local_table[idx];mfw;mfw=mfw_next) {
465
466
467
468 l=&mfw->hosts;
469 while((e=l->next) != l) {
470 h = list_entry(e, struct ip_masq_mfw_host, list);
471 atomic_dec(&mfw->nhosts);
472 list_del(&h->list);
473 kfree_s(h, sizeof(*h));
474 MOD_DEC_USE_COUNT;
475 }
476
477 if (atomic_read(&mfw->nhosts)) {
478 IP_MASQ_ERR("mfw_flush(): after flushing row nhosts=%d\n",
479 atomic_read(&mfw->nhosts));
480 }
481 mfw_next = mfw->next;
482 kfree_s(mfw, sizeof(*mfw));
483 MOD_DEC_USE_COUNT;
484 ip_masq_mod_dec_nent(mmod_self);
485 }
486 }
487}
488
489
490
491
492static int mfw_ctl(int optname, struct ip_masq_ctl *mctl, int optlen)
493{
494 struct ip_mfw_user *mu = &mctl->u.mfw_user;
495 struct ip_masq_mfw *mfw;
496 int ret = EINVAL;
497 int arglen = optlen - IP_MASQ_CTL_BSIZE;
498 int cmd;
499
500
501 IP_MASQ_DEBUG(1-debug, "ip_masq_user_ctl(len=%d/%d|%d/%d)\n",
502 arglen,
503 sizeof (*mu),
504 optlen,
505 sizeof (*mctl));
506
507
508
509
510 if (arglen != sizeof(*mu) && optlen != sizeof(*mctl))
511 return -EINVAL;
512
513
514
515
516 cmd = mctl->m_cmd;
517 IP_MASQ_DEBUG(1-debug, "ip_masq_mfw_ctl(cmd=%d, fwmark=%d)\n",
518 cmd, mu->fwmark);
519
520
521 switch(cmd) {
522 case IP_MASQ_CMD_NONE:
523 return 0;
524 case IP_MASQ_CMD_FLUSH:
525 break;
526 case IP_MASQ_CMD_ADD:
527 case IP_MASQ_CMD_INSERT:
528 case IP_MASQ_CMD_SET:
529 if (mu->fwmark == 0) {
530 IP_MASQ_DEBUG(1-debug, "invalid fwmark==0\n");
531 return -EINVAL;
532 }
533 if (mu->pref < 0) {
534 IP_MASQ_DEBUG(1-debug, "invalid pref==%d\n",
535 mu->pref);
536 return -EINVAL;
537 }
538 break;
539 }
540
541
542 ret = -EINVAL;
543
544 switch(cmd) {
545 case IP_MASQ_CMD_ADD:
546 case IP_MASQ_CMD_INSERT:
547 if (!mu->raddr) {
548 IP_MASQ_DEBUG(0-debug, "ip_masq_mfw_ctl(ADD): invalid redirect 0x%x:%d\n",
549 mu->raddr, mu->rport);
550 goto out;
551 }
552
553
554
555
556
557
558
559
560 down(&mfw_sema);
561
562 read_lock(&mfw_lock);
563 mfw = __mfw_get(mu->fwmark);
564 read_unlock(&mfw_lock);
565
566
567
568
569 if (mfw == NULL) {
570 mfw = mfw_new(mu->fwmark);
571 if (mfw == NULL)
572 ret = -ENOMEM;
573 }
574
575 if (mfw) {
576
577
578
579 ret = mfw_addhost(mfw, mu, cmd == IP_MASQ_CMD_ADD);
580
581
582
583
584
585 if (ret == 0 && atomic_read(&mfw->nhosts) == 1) {
586 write_lock_bh(&mfw_lock);
587 __mfw_add(mfw);
588 write_unlock_bh(&mfw_lock);
589 }
590 if (atomic_read(&mfw->nhosts) == 0) {
591 mfw_destroy(mfw);
592 }
593 }
594
595 up(&mfw_sema);
596
597 break;
598
599 case IP_MASQ_CMD_DEL:
600 down(&mfw_sema);
601
602 read_lock(&mfw_lock);
603 mfw = __mfw_get(mu->fwmark);
604 read_unlock(&mfw_lock);
605
606 if (mfw) {
607 ret = mfw_delhost(mfw, mu);
608
609
610
611
612
613 if (atomic_read(&mfw->nhosts) == 0) {
614 write_lock_bh(&mfw_lock);
615 __mfw_del(mfw);
616 write_unlock_bh(&mfw_lock);
617 mfw_destroy(mfw);
618 }
619 } else
620 ret = -ESRCH;
621
622 up(&mfw_sema);
623 break;
624 case IP_MASQ_CMD_FLUSH:
625
626 down(&mfw_sema);
627 mfw_flush();
628 up(&mfw_sema);
629 ret = 0;
630 break;
631 case IP_MASQ_CMD_SET:
632
633
634
635
636 read_lock(&mfw_lock);
637
638 mfw = __mfw_get(mu->fwmark);
639 if (mfw) {
640 write_lock_bh(&mfw->lock);
641
642 if (mu->flags & IP_MASQ_MFW_SCHED) {
643 struct ip_masq_mfw_host *h;
644 if ((h=__mfw_sched(mfw, 1))) {
645 mfw_host_to_user(h, mu);
646 ret = 0;
647 }
648 } else {
649 ret = __mfw_edithost(mfw, mu);
650 }
651
652 write_unlock_bh(&mfw->lock);
653 }
654
655 read_unlock(&mfw_lock);
656 break;
657 }
658out:
659
660 return ret;
661}
662
663
664
665
666
667
668
669
670
671static int mfw_in_rule(const struct sk_buff *skb, const struct iphdr *iph)
672{
673 int val;
674 read_lock(&mfw_lock);
675 val = ( __mfw_get(skb->fwmark) != 0);
676 read_unlock(&mfw_lock);
677 return val;
678}
679
680
681
682
683static struct ip_masq * mfw_in_create(const struct sk_buff *skb, const struct iphdr *iph, __u32 maddr)
684{
685 union ip_masq_tphdr tph;
686 struct ip_masq *ms = NULL;
687 struct ip_masq_mfw_host *h = NULL;
688
689 tph.raw = (char*) iph + iph->ihl * 4;
690
691 switch (iph->protocol) {
692 case IPPROTO_TCP:
693
694
695
696 if (!tph.th->syn && tph.th->ack)
697 return NULL;
698 case IPPROTO_UDP:
699 break;
700 default:
701 return NULL;
702 }
703
704
705
706
707
708
709
710 if ((h=mfw_lookup(skb->fwmark))) {
711 ms = ip_masq_new(iph->protocol,
712 iph->daddr, tph.portp[1],
713
714 h->addr, h->port? h->port : tph.portp[1],
715 iph->saddr, tph.portp[0],
716 0);
717
718 if (ms != NULL)
719 ip_masq_listen(ms);
720 }
721 return ms;
722}
723
724
725#define mfw_in_update NULL
726#define mfw_out_rule NULL
727#define mfw_out_create NULL
728#define mfw_out_update NULL
729
730static struct ip_masq_mod mfw_mod = {
731 NULL,
732 NULL,
733 "mfw",
734 ATOMIC_INIT(0),
735 ATOMIC_INIT(0),
736 proc_ent,
737 mfw_ctl,
738 NULL,
739 NULL,
740 mfw_in_rule,
741 mfw_in_update,
742 mfw_in_create,
743 mfw_out_rule,
744 mfw_out_update,
745 mfw_out_create,
746};
747
748
749__initfunc(int ip_mfw_init(void))
750{
751 return register_ip_masq_mod ((mmod_self=&mfw_mod));
752}
753
754int ip_mfw_done(void)
755{
756 return unregister_ip_masq_mod(&mfw_mod);
757}
758
759#ifdef MODULE
760EXPORT_NO_SYMBOLS;
761
762int init_module(void)
763{
764 if (ip_mfw_init() != 0)
765 return -EIO;
766 return 0;
767}
768
769void cleanup_module(void)
770{
771 if (ip_mfw_done() != 0)
772 printk(KERN_INFO "can't remove module");
773}
774
775#endif
776