linux-old/net/ipv4/ip_masq_portfw.c
<<
>>
Prefs
   1/*
   2 *              IP_MASQ_PORTFW masquerading module
   3 *
   4 *
   5 *      $Id: ip_masq_portfw.c,v 1.3 1998/12/08 05:42:12 davem Exp $
   6 *
   7 * Author:      Steven Clarke <steven.clarke@monmouth.demon.co.uk>
   8 *
   9 * Fixes:       
  10 *      Juan Jose Ciarlante     : created this new file from ip_masq.c and ip_fw.c
  11 *      Juan Jose Ciarlante     : modularized 
  12 *      Juan Jose Ciarlante     : use GFP_KERNEL
  13 *      Juan Jose Ciarlante     : locking
  14 *
  15 */
  16
  17#include <linux/config.h>
  18#include <linux/module.h>
  19#include <linux/types.h>
  20#include <linux/kernel.h>
  21#include <linux/errno.h>
  22#include <linux/list.h>
  23#include <net/ip.h>
  24#include <linux/ip_fw.h>
  25#include <linux/ip_masq.h>
  26#include <net/ip_masq.h>
  27#include <net/ip_masq_mod.h>
  28#include <linux/proc_fs.h>
  29#include <linux/init.h>
  30
  31#define IP_PORTFW_PORT_MIN 1
  32#define IP_PORTFW_PORT_MAX 60999
  33
  34struct ip_portfw {
  35        struct          list_head list;
  36        __u32           laddr, raddr;
  37        __u16           lport, rport;
  38        atomic_t        pref_cnt;       /* pref "counter" down to 0 */
  39        int             pref;           /* user set pref */
  40};
  41
  42static struct ip_masq_mod *mmod_self = NULL;
  43/*
  44 *      Debug level
  45 */
  46#ifdef CONFIG_IP_MASQ_DEBUG
  47static int debug=0;
  48MODULE_PARM(debug, "i");
  49#endif
  50
  51/*
  52 *      Lock
  53 */
  54#ifdef __SMP__
  55static spinlock_t portfw_lock = SPIN_LOCK_UNLOCKED;
  56#endif
  57
  58static struct list_head portfw_list[2];
  59static __inline__ int portfw_idx(int protocol)
  60{
  61        return (protocol==IPPROTO_TCP);
  62}
  63
  64/*
  65 *
  66 *      Delete forwarding entry(s):
  67 *      called from _DEL, u-space.
  68 *      . "relaxed" match, except for lport
  69 *
  70 */
  71
  72static __inline__ int ip_portfw_del(__u16 protocol, __u16 lport, __u32 laddr, __u16 rport, __u32 raddr)
  73{
  74        int prot = portfw_idx(protocol);
  75        struct ip_portfw *n;
  76        struct list_head *entry;
  77        struct list_head *list = &portfw_list[prot];
  78        int nent;
  79
  80        nent = atomic_read(&mmod_self->mmod_nent);
  81
  82        write_lock_bh(&portfw_lock);
  83
  84        for (entry=list->next;entry != list;entry = entry->next)  {
  85                n = list_entry(entry, struct ip_portfw, list);
  86                if (n->lport == lport && 
  87                                (!laddr || n->laddr == laddr) &&
  88                                (!raddr || n->raddr == raddr) && 
  89                                (!rport || n->rport == rport)) {
  90                        list_del(entry);
  91                        ip_masq_mod_dec_nent(mmod_self);
  92                        kfree_s(n, sizeof(struct ip_portfw));
  93                        MOD_DEC_USE_COUNT;
  94                }
  95        }
  96        write_unlock_bh(&portfw_lock);
  97        
  98        return nent==atomic_read(&mmod_self->mmod_nent)? ESRCH : 0;
  99}
 100
 101/*
 102 *      Flush tables
 103 *      called from _FLUSH, u-space.
 104 */
 105static __inline__ void ip_portfw_flush(void)
 106{
 107        int prot;
 108        struct list_head *l;
 109        struct list_head *e;
 110        struct ip_portfw *n;
 111
 112        write_lock_bh(&portfw_lock);
 113
 114        for (prot = 0; prot < 2;prot++) {
 115                l = &portfw_list[prot];
 116                while((e=l->next) != l) {
 117                        ip_masq_mod_dec_nent(mmod_self);
 118                        n = list_entry (e, struct ip_portfw, list);
 119                        list_del(e);
 120                        kfree_s(n, sizeof (*n));
 121                        MOD_DEC_USE_COUNT;
 122                }
 123        }
 124
 125        write_unlock_bh(&portfw_lock);
 126}
 127
 128/*
 129 *      Lookup routine for lport,laddr match
 130 *      must be called with locked tables
 131 */
 132static __inline__ struct ip_portfw *ip_portfw_lookup(__u16 protocol, __u16 lport, __u32 laddr, __u32 *daddr_p, __u16 *dport_p)
 133{
 134        int prot = portfw_idx(protocol);
 135        
 136        struct ip_portfw *n = NULL;
 137        struct list_head *l, *e;
 138
 139        l = &portfw_list[prot];
 140
 141        for (e=l->next;e!=l;e=e->next) {
 142                n = list_entry(e, struct ip_portfw, list);
 143                if (lport == n->lport && laddr == n->laddr) {
 144                        /* Please be nice, don't pass only a NULL dport */
 145                        if (daddr_p) {
 146                                *daddr_p = n->raddr;
 147                                *dport_p = n->rport;
 148                        }
 149                        
 150                        goto out;
 151                }
 152        }
 153        n = NULL;
 154out:
 155        return n;
 156}
 157
 158/*
 159 *      Edit routine for lport,[laddr], [raddr], [rport] match
 160 *      By now, only called from u-space
 161 */
 162static __inline__ int ip_portfw_edit(__u16 protocol, __u16 lport, __u32 laddr, __u16 rport, __u32 raddr, int pref)
 163{
 164        int prot = portfw_idx(protocol);
 165        
 166        struct ip_portfw *n = NULL;
 167        struct list_head *l, *e;
 168        int count = 0;
 169
 170
 171        read_lock_bh(&portfw_lock);
 172
 173        l = &portfw_list[prot];
 174
 175        for (e=l->next;e!=l;e=e->next) {
 176                n = list_entry(e, struct ip_portfw, list);
 177                if (lport == n->lport && 
 178                                (!laddr || laddr == n->laddr) &&
 179                                (!rport || rport == n->rport) && 
 180                                (!raddr || raddr == n->raddr)) {
 181                        n->pref = pref;
 182                        atomic_set(&n->pref_cnt, pref);
 183                        count++;
 184                }
 185        }
 186
 187        read_unlock_bh(&portfw_lock);
 188
 189        return count;
 190}
 191
 192/*
 193 *      Add/edit en entry
 194 *      called from _ADD, u-space.
 195 *      must return 0 or +errno
 196 */
 197static __inline__ int ip_portfw_add(__u16 protocol, __u16 lport, __u32 laddr, __u16 rport, __u32 raddr, int pref)
 198{
 199        struct ip_portfw  *npf;
 200        int prot = portfw_idx(protocol);
 201         
 202        if (pref <= 0)
 203                return EINVAL;
 204
 205        if (ip_portfw_edit(protocol, lport, laddr, rport, raddr, pref)) {
 206                /*
 207                 *      Edit ok ...
 208                 */
 209                return 0;
 210        }
 211
 212        /* may block ... */
 213        npf = (struct ip_portfw*) kmalloc(sizeof(struct ip_portfw), GFP_KERNEL);
 214
 215        if (!npf)
 216                return ENOMEM;
 217
 218        MOD_INC_USE_COUNT;
 219        memset(npf, 0, sizeof(*npf));
 220
 221        npf->laddr = laddr;
 222        npf->lport = lport;
 223        npf->rport = rport;
 224        npf->raddr = raddr;
 225        npf->pref  = pref;
 226
 227        atomic_set(&npf->pref_cnt, npf->pref);
 228        INIT_LIST_HEAD(&npf->list);
 229
 230        write_lock_bh(&portfw_lock);
 231
 232        /*
 233         *      Add at head
 234         */
 235        list_add(&npf->list, &portfw_list[prot]);
 236
 237        write_unlock_bh(&portfw_lock);
 238
 239        ip_masq_mod_inc_nent(mmod_self);
 240        return 0;
 241}
 242
 243
 244
 245static __inline__ int portfw_ctl(int optname, struct ip_masq_ctl *mctl, int optlen)
 246{
 247        struct ip_portfw_user *mm =  &mctl->u.portfw_user;
 248        int ret = EINVAL;
 249        int arglen = optlen - IP_MASQ_CTL_BSIZE;
 250        int cmd;
 251
 252
 253        IP_MASQ_DEBUG(1-debug, "ip_masq_user_ctl(len=%d/%d|%d/%d)\n",
 254                arglen,
 255                sizeof (*mm),
 256                optlen,
 257                sizeof (*mctl));
 258
 259        /*
 260         *      Yes, I'm a bad guy ...
 261         */
 262        if (arglen != sizeof(*mm) && optlen != sizeof(*mctl)) 
 263                return EINVAL;
 264 
 265        /* 
 266         *      Don't trust the lusers - plenty of error checking! 
 267         */
 268        cmd = mctl->m_cmd;
 269        IP_MASQ_DEBUG(1-debug, "ip_masq_portfw_ctl(cmd=%d)\n", cmd);
 270
 271
 272        switch (cmd) {
 273                case IP_MASQ_CMD_NONE:
 274                        return 0;
 275                case IP_MASQ_CMD_FLUSH:
 276                        break;
 277                default:
 278                        if (htons(mm->lport) < IP_PORTFW_PORT_MIN || htons(mm->lport) > IP_PORTFW_PORT_MAX)
 279                                return EINVAL;
 280
 281                        if (mm->protocol!=IPPROTO_TCP && mm->protocol!=IPPROTO_UDP)
 282                                return EINVAL;
 283        }
 284
 285        switch(cmd) {
 286        case IP_MASQ_CMD_ADD:
 287                ret = ip_portfw_add(mm->protocol,
 288                                mm->lport, mm->laddr,
 289                                mm->rport, mm->raddr,
 290                                mm->pref);
 291                break;
 292
 293        case IP_MASQ_CMD_DEL:
 294                ret = ip_portfw_del(mm->protocol, 
 295                                mm->lport, mm->laddr,
 296                                mm->rport, mm->raddr);
 297                break;
 298        case IP_MASQ_CMD_FLUSH:
 299                ip_portfw_flush();
 300                ret = 0;
 301                break;
 302        }
 303                                
 304
 305        return ret;
 306}
 307
 308
 309
 310
 311#ifdef CONFIG_PROC_FS
 312
 313static int portfw_procinfo(char *buffer, char **start, off_t offset,
 314                            int length, int unused)
 315{
 316        off_t pos=0, begin;
 317        struct ip_portfw *pf;
 318        struct list_head *l, *e;
 319        char temp[65];
 320        int ind;
 321        int len=0;
 322
 323
 324        if (offset < 64) 
 325        {
 326                sprintf(temp, "Prot LAddr    LPort > RAddr    RPort PrCnt  Pref");
 327                len = sprintf(buffer, "%-63s\n", temp);
 328        }
 329        pos = 64;
 330
 331        read_lock_bh(&portfw_lock);
 332
 333        for(ind = 0; ind < 2; ind++)
 334        {
 335                l = &portfw_list[ind];
 336                for (e=l->next; e!=l; e=e->next)
 337                {
 338                        pf = list_entry(e, struct ip_portfw, list);
 339                        pos += 64;
 340                        if (pos <= offset) {
 341                                len = 0;
 342                                continue;
 343                        }
 344
 345                        sprintf(temp,"%s  %08lX %5u > %08lX %5u %5d %5d",
 346                                ind ? "TCP" : "UDP",
 347                                ntohl(pf->laddr), ntohs(pf->lport),
 348                                ntohl(pf->raddr), ntohs(pf->rport),
 349                                atomic_read(&pf->pref_cnt), pf->pref);
 350                        len += sprintf(buffer+len, "%-63s\n", temp);
 351
 352                        if (len >= length)
 353                                goto done;
 354                }
 355        }
 356done:
 357        read_unlock_bh(&portfw_lock);
 358
 359        begin = len - (pos - offset);
 360        *start = buffer + begin;
 361        len -= begin;
 362        if(len>length)
 363                len = length;
 364        return len;
 365}
 366
 367static struct proc_dir_entry portfw_proc_entry = {
 368/*              0, 0, NULL", */
 369                0, 6, "portfw",   /* Just for compatibility, for now ... */
 370                S_IFREG | S_IRUGO, 1, 0, 0,
 371                0, &proc_net_inode_operations,
 372                portfw_procinfo
 373};
 374
 375#define proc_ent &portfw_proc_entry
 376#else /* !CONFIG_PROC_FS */
 377
 378#define proc_ent NULL
 379#endif
 380
 381static int portfw_in_rule(const struct sk_buff *skb, const struct iphdr *iph)
 382{
 383        const __u16 *portp = (__u16 *)&(((char *)iph)[iph->ihl*4]);
 384#ifdef CONFIG_IP_MASQ_DEBUG
 385        struct rtable *rt = (struct rtable *)skb->dst;
 386#endif
 387        struct ip_portfw *pfw;
 388
 389        IP_MASQ_DEBUG(2, "portfw_in_rule(): skb:= dev=%s (index=%d), rt_iif=%d, rt_flags=0x%x rt_dev___=%s daddr=%d.%d.%d.%d dport=%d\n",
 390                skb->dev->name, skb->dev->ifindex, rt->rt_iif, rt->rt_flags,
 391                rt->u.dst.dev->name,
 392                NIPQUAD(iph->daddr), ntohs(portp[1]));
 393
 394        read_lock(&portfw_lock);
 395        pfw = ip_portfw_lookup(iph->protocol, portp[1], iph->daddr, NULL, NULL);
 396        read_unlock(&portfw_lock);
 397        return (pfw!=0);
 398}
 399
 400static struct ip_masq * portfw_in_create(const struct sk_buff *skb, const struct iphdr *iph, __u32 maddr)
 401{
 402        /* 
 403         *      If no entry exists in the masquerading table
 404         *      and the port is involved
 405         *      in port forwarding, create a new masq entry 
 406         */
 407
 408        __u32 raddr;
 409        __u16 rport;
 410        const __u16 *portp = (__u16 *)&(((char *)iph)[iph->ihl*4]);
 411        struct ip_masq *ms = NULL;
 412        struct ip_portfw *pf;
 413
 414        /*
 415         *      Lock for writing.
 416         */
 417        write_lock(&portfw_lock);
 418
 419        if ((pf=ip_portfw_lookup(iph->protocol, 
 420                        portp[1], iph->daddr, 
 421                        &raddr, &rport))) {
 422                ms = ip_masq_new(iph->protocol,
 423                                iph->daddr, portp[1],   
 424                                raddr, rport,
 425                                iph->saddr, portp[0],
 426                                0);
 427                ip_masq_listen(ms);
 428
 429                if (!ms || atomic_read(&mmod_self->mmod_nent) <= 1 
 430                        /* || ip_masq_nlocks(&portfw_lock) != 1 */ )
 431                                /*
 432                                 *      Maybe later...
 433                                 */
 434                                goto out;
 435
 436                /*
 437                 *      Entry created, lock==1.
 438                 *      if pref_cnt == 0, move
 439                 *      entry at _tail_.
 440                 *      This is a simple load balance scheduling
 441                 */
 442        
 443                if (atomic_dec_and_test(&pf->pref_cnt)) {
 444
 445                        atomic_set(&pf->pref_cnt, pf->pref);
 446                        list_del(&pf->list);
 447                        list_add(&pf->list, 
 448                                portfw_list[portfw_idx(iph->protocol)].prev);
 449
 450                }
 451        }
 452out:
 453        write_unlock(&portfw_lock);
 454        return ms;
 455}
 456
 457#define portfw_in_update        NULL
 458#define portfw_out_rule         NULL
 459#define portfw_out_create       NULL
 460#define portfw_out_update       NULL
 461
 462static struct ip_masq_mod portfw_mod = {
 463        NULL,                   /* next */
 464        NULL,                   /* next_reg */
 465        "portfw",               /* name */
 466        ATOMIC_INIT(0),         /* nent */
 467        ATOMIC_INIT(0),         /* refcnt */
 468        proc_ent,
 469        portfw_ctl,
 470        NULL,                   /* masq_mod_init */
 471        NULL,                   /* masq_mod_done */
 472        portfw_in_rule,
 473        portfw_in_update,
 474        portfw_in_create,
 475        portfw_out_rule,
 476        portfw_out_update,
 477        portfw_out_create,
 478};
 479
 480
 481
 482__initfunc(int ip_portfw_init(void))
 483{
 484        INIT_LIST_HEAD(&portfw_list[0]);
 485        INIT_LIST_HEAD(&portfw_list[1]);
 486        return register_ip_masq_mod ((mmod_self=&portfw_mod));
 487}
 488
 489int ip_portfw_done(void)
 490{
 491        return unregister_ip_masq_mod(&portfw_mod);
 492}
 493
 494#ifdef MODULE
 495EXPORT_NO_SYMBOLS;
 496
 497int init_module(void)
 498{
 499        if (ip_portfw_init() != 0)
 500                return -EIO;
 501        return 0;
 502}
 503
 504void cleanup_module(void)
 505{
 506        if (ip_portfw_done() != 0)
 507                printk(KERN_INFO "ip_portfw_done(): can't remove module");
 508}
 509
 510#endif /* MODULE */
 511
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.