linux-old/net/ipv4/ip_masq_app.c
<<
>>
Prefs
   1/*
   2 *              IP_MASQ_APP application masquerading module
   3 *
   4 *
   5 *      $Id: ip_masq_app.c,v 1.16 1998/08/29 23:51:14 davem Exp $
   6 *
   7 * Author:      Juan Jose Ciarlante, <jjciarla@raiz.uncu.edu.ar>
   8 *
   9 *
  10 *      This program is free software; you can redistribute it and/or
  11 *      modify it under the terms of the GNU General Public License
  12 *      as published by the Free Software Foundation; either version
  13 *      2 of the License, or (at your option) any later version.
  14 *
  15 * Fixes:
  16 *      JJC                     : Implemented also input pkt hook
  17 *      Miquel van Smoorenburg  : Copy more stuff when resizing skb
  18 *
  19 *
  20 * FIXME:
  21 *      - ip_masq_skb_replace(): use same skb if space available.
  22 *
  23 */
  24
  25#include <linux/config.h>
  26#include <linux/module.h>
  27#include <linux/types.h>
  28#include <linux/kernel.h>
  29#include <linux/errno.h>
  30#include <linux/skbuff.h>
  31#include <linux/in.h>
  32#include <linux/ip.h>
  33#include <linux/init.h>
  34#include <net/protocol.h>
  35#include <net/tcp.h>
  36#include <net/udp.h>
  37#include <asm/system.h>
  38#include <linux/stat.h>
  39#include <linux/proc_fs.h>
  40#include <net/ip_masq.h>
  41
  42#define IP_MASQ_APP_TAB_SIZE  16 /* must be power of 2 */
  43
  44#define IP_MASQ_APP_HASH(proto, port) ((port^proto) & (IP_MASQ_APP_TAB_SIZE-1))
  45#define IP_MASQ_APP_TYPE(proto, port) ( proto<<16 | port )
  46#define IP_MASQ_APP_PORT(type)        ( type & 0xffff )
  47#define IP_MASQ_APP_PROTO(type)       ( (type>>16) & 0x00ff )
  48
  49
  50EXPORT_SYMBOL(register_ip_masq_app);
  51EXPORT_SYMBOL(unregister_ip_masq_app);
  52EXPORT_SYMBOL(ip_masq_skb_replace);
  53
  54/*
  55 *      will hold masq app. hashed list heads
  56 */
  57
  58struct ip_masq_app *ip_masq_app_base[IP_MASQ_APP_TAB_SIZE];
  59
  60/*
  61 *      ip_masq_app registration routine
  62 *      port: host byte order.
  63 */
  64
  65int register_ip_masq_app(struct ip_masq_app *mapp, unsigned short proto, __u16 port)
  66{
  67        unsigned long flags;
  68        unsigned hash;
  69        if (!mapp) {
  70                IP_MASQ_ERR("register_ip_masq_app(): NULL arg\n");
  71                return -EINVAL;
  72        }
  73        mapp->type = IP_MASQ_APP_TYPE(proto, port);
  74        mapp->n_attach = 0;
  75        hash = IP_MASQ_APP_HASH(proto, port);
  76
  77        save_flags(flags);
  78        cli();
  79        mapp->next = ip_masq_app_base[hash];
  80        ip_masq_app_base[hash] = mapp;
  81        restore_flags(flags);
  82
  83        return 0;
  84}
  85
  86/*
  87 *      ip_masq_app unreg. routine.
  88 */
  89
  90int unregister_ip_masq_app(struct ip_masq_app *mapp)
  91{
  92        struct ip_masq_app **mapp_p;
  93        unsigned hash;
  94        unsigned long flags;
  95        if (!mapp) {
  96                IP_MASQ_ERR("unregister_ip_masq_app(): NULL arg\n");
  97                return -EINVAL;
  98        }
  99        /*
 100         * only allow unregistration if it has no attachments
 101         */
 102        if (mapp->n_attach)  {
 103                IP_MASQ_ERR("unregister_ip_masq_app(): has %d attachments. failed\n",
 104                       mapp->n_attach);
 105                return -EINVAL;
 106        }
 107        hash = IP_MASQ_APP_HASH(IP_MASQ_APP_PROTO(mapp->type), IP_MASQ_APP_PORT(mapp->type));
 108
 109        save_flags(flags);
 110        cli();
 111        for (mapp_p = &ip_masq_app_base[hash]; *mapp_p ; mapp_p = &(*mapp_p)->next)
 112                if (mapp == (*mapp_p))  {
 113                        *mapp_p = mapp->next;
 114                        restore_flags(flags);
 115                        return 0;
 116                }
 117
 118        restore_flags(flags);
 119        IP_MASQ_ERR("unregister_ip_masq_app(proto=%s,port=%u): not hashed!\n",
 120               masq_proto_name(IP_MASQ_APP_PROTO(mapp->type)), IP_MASQ_APP_PORT(mapp->type));
 121        return -EINVAL;
 122}
 123
 124/*
 125 *      get ip_masq_app object by its proto and port (net byte order).
 126 */
 127
 128struct ip_masq_app * ip_masq_app_get(unsigned short proto, __u16 port)
 129{
 130        struct ip_masq_app *mapp;
 131        unsigned hash;
 132        unsigned type;
 133
 134        port = ntohs(port);
 135        type = IP_MASQ_APP_TYPE(proto,port);
 136        hash = IP_MASQ_APP_HASH(proto,port);
 137        for(mapp = ip_masq_app_base[hash]; mapp ; mapp = mapp->next) {
 138                if (type == mapp->type) return mapp;
 139        }
 140        return NULL;
 141}
 142
 143/*
 144 *      ip_masq_app object binding related funcs.
 145 */
 146
 147/*
 148 *      change ip_masq_app object's number of bindings
 149 */
 150
 151static __inline__ int ip_masq_app_bind_chg(struct ip_masq_app *mapp, int delta)
 152{
 153        unsigned long flags;
 154        int n_at;
 155        if (!mapp) return -1;
 156        save_flags(flags);
 157        cli();
 158        n_at = mapp->n_attach + delta;
 159        if (n_at < 0) {
 160                restore_flags(flags);
 161                IP_MASQ_ERR("ip_masq_app: tried to set n_attach < 0 for (proto=%s,port==%d) ip_masq_app object.\n",
 162                       masq_proto_name(IP_MASQ_APP_PROTO(mapp->type)),
 163                       IP_MASQ_APP_PORT(mapp->type));
 164                return -1;
 165        }
 166        mapp->n_attach = n_at;
 167        restore_flags(flags);
 168        return 0;
 169}
 170
 171/*
 172 *      Bind ip_masq to its ip_masq_app based on proto and dport ALREADY
 173 *      set in ip_masq struct. Also calls constructor.
 174 */
 175
 176struct ip_masq_app * ip_masq_bind_app(struct ip_masq *ms)
 177{
 178        struct ip_masq_app * mapp;
 179
 180        if (ms->protocol != IPPROTO_TCP && ms->protocol != IPPROTO_UDP)
 181                return NULL;
 182
 183        mapp = ip_masq_app_get(ms->protocol, ms->dport);
 184
 185#if 0000
 186/* #ifdef CONFIG_IP_MASQUERADE_IPAUTOFW */
 187        if (mapp == NULL)
 188                mapp = ip_masq_app_get(ms->protocol, ms->sport);
 189/* #endif */
 190#endif
 191
 192        if (mapp != NULL) {
 193                /*
 194                 *      don't allow binding if already bound
 195                 */
 196
 197                if (ms->app != NULL) {
 198                        IP_MASQ_ERR("ip_masq_bind_app() called for already bound object.\n");
 199                        return ms->app;
 200                }
 201
 202                ms->app = mapp;
 203                if (mapp->masq_init_1) mapp->masq_init_1(mapp, ms);
 204                ip_masq_app_bind_chg(mapp, +1);
 205        }
 206        return mapp;
 207}
 208
 209/*
 210 *      Unbind ms from type object and call ms destructor (does not kfree()).
 211 */
 212
 213int ip_masq_unbind_app(struct ip_masq *ms)
 214{
 215        struct ip_masq_app * mapp;
 216        mapp = ms->app;
 217
 218        if (ms->protocol != IPPROTO_TCP && ms->protocol != IPPROTO_UDP)
 219                return 0;
 220
 221        if (mapp != NULL) {
 222                if (mapp->masq_done_1) mapp->masq_done_1(mapp, ms);
 223                ms->app = NULL;
 224                ip_masq_app_bind_chg(mapp, -1);
 225        }
 226        return (mapp != NULL);
 227}
 228
 229/*
 230 *      Fixes th->seq based on ip_masq_seq info.
 231 */
 232
 233static __inline__ void masq_fix_seq(const struct ip_masq_seq *ms_seq, struct tcphdr *th)
 234{
 235        __u32 seq;
 236
 237        seq = ntohl(th->seq);
 238
 239        /*
 240         *      Adjust seq with delta-offset for all packets after
 241         *      the most recent resized pkt seq and with previous_delta offset
 242         *      for all packets before most recent resized pkt seq.
 243         */
 244
 245        if (ms_seq->delta || ms_seq->previous_delta) {
 246                if(after(seq,ms_seq->init_seq) ) {
 247                        th->seq = htonl(seq + ms_seq->delta);
 248                        IP_MASQ_DEBUG(1, "masq_fix_seq() : added delta (%d) to seq\n",ms_seq->delta);
 249                } else {
 250                        th->seq = htonl(seq + ms_seq->previous_delta);
 251                        IP_MASQ_DEBUG(1, "masq_fix_seq() : added previous_delta (%d) to seq\n",ms_seq->previous_delta);
 252                }
 253        }
 254
 255
 256}
 257
 258/*
 259 *      Fixes th->ack_seq based on ip_masq_seq info.
 260 */
 261
 262static __inline__ void masq_fix_ack_seq(const struct ip_masq_seq *ms_seq, struct tcphdr *th)
 263{
 264        __u32 ack_seq;
 265
 266        ack_seq=ntohl(th->ack_seq);
 267
 268        /*
 269         * Adjust ack_seq with delta-offset for
 270         * the packets AFTER most recent resized pkt has caused a shift
 271         * for packets before most recent resized pkt, use previous_delta
 272         */
 273
 274        if (ms_seq->delta || ms_seq->previous_delta) {
 275                if(after(ack_seq,ms_seq->init_seq)) {
 276                        th->ack_seq = htonl(ack_seq-ms_seq->delta);
 277                        IP_MASQ_DEBUG(1, "masq_fix_ack_seq() : subtracted delta (%d) from ack_seq\n",ms_seq->delta);
 278
 279                } else {
 280                        th->ack_seq = htonl(ack_seq-ms_seq->previous_delta);
 281                        IP_MASQ_DEBUG(1, "masq_fix_ack_seq() : subtracted previous_delta (%d) from ack_seq\n",ms_seq->previous_delta);
 282                }
 283        }
 284
 285}
 286
 287/*
 288 *      Updates ip_masq_seq if pkt has been resized
 289 *      Assumes already checked proto==IPPROTO_TCP and diff!=0.
 290 */
 291
 292static __inline__ void masq_seq_update(struct ip_masq *ms, struct ip_masq_seq *ms_seq, unsigned mflag, __u32 seq, int diff)
 293{
 294        /* if (diff == 0) return; */
 295
 296        if ( !(ms->flags & mflag) || after(seq, ms_seq->init_seq))
 297        {
 298                ms_seq->previous_delta=ms_seq->delta;
 299                ms_seq->delta+=diff;
 300                ms_seq->init_seq=seq;
 301                ms->flags |= mflag;
 302        }
 303}
 304
 305/*
 306 *      Output pkt hook. Will call bound ip_masq_app specific function
 307 *      called by ip_fw_masquerade(), assumes previously checked ms!=NULL
 308 *      returns (new - old) skb->len diff.
 309 */
 310
 311int ip_masq_app_pkt_out(struct ip_masq *ms, struct sk_buff **skb_p, __u32 maddr)
 312{
 313        struct ip_masq_app * mapp;
 314        struct iphdr *iph;
 315        struct tcphdr *th;
 316        int diff;
 317        __u32 seq;
 318
 319        /*
 320         *      check if application masquerading is bound to
 321         *      this ip_masq.
 322         *      assumes that once an ip_masq is bound,
 323         *      it will not be unbound during its life.
 324         */
 325
 326        if ( (mapp = ms->app) == NULL)
 327                return 0;
 328
 329        iph = (*skb_p)->nh.iph;
 330        th = (struct tcphdr *)&(((char *)iph)[iph->ihl*4]);
 331
 332        /*
 333         *      Remember seq number in case this pkt gets resized
 334         */
 335
 336        seq = ntohl(th->seq);
 337
 338        /*
 339         *      Fix seq stuff if flagged as so.
 340         */
 341
 342        if (ms->protocol == IPPROTO_TCP) {
 343                if (ms->flags & IP_MASQ_F_OUT_SEQ)
 344                        masq_fix_seq(&ms->out_seq, th);
 345                if (ms->flags & IP_MASQ_F_IN_SEQ)
 346                        masq_fix_ack_seq(&ms->in_seq, th);
 347        }
 348
 349        /*
 350         *      Call private output hook function
 351         */
 352
 353        if ( mapp->pkt_out == NULL )
 354                return 0;
 355
 356        diff = mapp->pkt_out(mapp, ms, skb_p, maddr);
 357
 358        /*
 359         *      Update ip_masq seq stuff if len has changed.
 360         */
 361
 362        if (diff != 0 && ms->protocol == IPPROTO_TCP)
 363                masq_seq_update(ms, &ms->out_seq, IP_MASQ_F_OUT_SEQ, seq, diff);
 364
 365        return diff;
 366}
 367
 368/*
 369 *      Input pkt hook. Will call bound ip_masq_app specific function
 370 *      called by ip_fw_demasquerade(), assumes previously checked ms!=NULL.
 371 *      returns (new - old) skb->len diff.
 372 */
 373
 374int ip_masq_app_pkt_in(struct ip_masq *ms, struct sk_buff **skb_p, __u32 maddr)
 375{
 376        struct ip_masq_app * mapp;
 377        struct iphdr *iph;
 378        struct tcphdr *th;
 379        int diff;
 380        __u32 seq;
 381
 382        /*
 383         *      check if application masquerading is bound to
 384         *      this ip_masq.
 385         *      assumes that once an ip_masq is bound,
 386         *      it will not be unbound during its life.
 387         */
 388
 389        if ( (mapp = ms->app) == NULL)
 390                return 0;
 391
 392        iph = (*skb_p)->nh.iph;
 393        th = (struct tcphdr *)&(((char *)iph)[iph->ihl*4]);
 394
 395        /*
 396         *      Remember seq number in case this pkt gets resized
 397         */
 398
 399        seq = ntohl(th->seq);
 400
 401        /*
 402         *      Fix seq stuff if flagged as so.
 403         */
 404
 405        if (ms->protocol == IPPROTO_TCP) {
 406                if (ms->flags & IP_MASQ_F_IN_SEQ)
 407                        masq_fix_seq(&ms->in_seq, th);
 408                if (ms->flags & IP_MASQ_F_OUT_SEQ)
 409                        masq_fix_ack_seq(&ms->out_seq, th);
 410        }
 411
 412        /*
 413         *      Call private input hook function
 414         */
 415
 416        if ( mapp->pkt_in == NULL )
 417                return 0;
 418
 419        diff = mapp->pkt_in(mapp, ms, skb_p, maddr);
 420
 421        /*
 422         *      Update ip_masq seq stuff if len has changed.
 423         */
 424
 425        if (diff != 0 && ms->protocol == IPPROTO_TCP)
 426                masq_seq_update(ms, &ms->in_seq, IP_MASQ_F_IN_SEQ, seq, diff);
 427
 428        return diff;
 429}
 430
 431/*
 432 *      /proc/ip_masq_app entry function
 433 */
 434
 435int ip_masq_app_getinfo(char *buffer, char **start, off_t offset, int length, int dummy)
 436{
 437        off_t pos=0, begin=0;
 438        int len=0;
 439        struct ip_masq_app * mapp;
 440        unsigned idx;
 441
 442        if (offset < 40)
 443                len=sprintf(buffer,"%-39s\n", "prot port    n_attach name");
 444        pos = 40;
 445
 446        for (idx=0 ; idx < IP_MASQ_APP_TAB_SIZE; idx++)
 447                for (mapp = ip_masq_app_base[idx]; mapp ; mapp = mapp->next) {
 448                        /*
 449                         * If you change the length of this sprintf, then all
 450                         * the length calculations need fixing too!
 451                         * Line length = 40 (3 + 2 + 7 + 1 + 7 + 1 + 2 + 17)
 452                         */
 453                        pos += 40;
 454                        if (pos < offset)
 455                                continue;
 456
 457                        len += sprintf(buffer+len, "%-3s  %-7u %-7d  %-17s\n",
 458                                       masq_proto_name(IP_MASQ_APP_PROTO(mapp->type)),
 459                                       IP_MASQ_APP_PORT(mapp->type), mapp->n_attach,
 460                                       mapp->name);
 461
 462                        if(len >= length)
 463                                goto done;
 464                }
 465done:
 466        begin = len - (pos - offset);
 467        *start = buffer + begin;
 468        len -= begin;
 469        if (len > length)
 470                len = length;
 471        return len;
 472}
 473
 474
 475#ifdef CONFIG_PROC_FS
 476static struct proc_dir_entry proc_net_ip_masq_app = {
 477        PROC_NET_IP_MASQ_APP, 3, "app",
 478        S_IFREG | S_IRUGO, 1, 0, 0,
 479        0, &proc_net_inode_operations,
 480        ip_masq_app_getinfo
 481};
 482#endif
 483
 484/*
 485 *      Initialization routine
 486 */
 487
 488__initfunc(int ip_masq_app_init(void))
 489{
 490#ifdef CONFIG_PROC_FS
 491        ip_masq_proc_register(&proc_net_ip_masq_app);
 492#endif
 493        return 0;
 494}
 495
 496/*
 497 *      Replace a segment (of skb->data) with a new one.
 498 *      FIXME: Should re-use same skb if space available, this could
 499 *             be done if n_len < o_len, unless some extra space
 500 *             were already allocated at driver level :P .
 501 */
 502
 503static struct sk_buff * skb_replace(struct sk_buff *skb, int pri, char *o_buf, int o_len, char *n_buf, int n_len)
 504{
 505        int maxsize, diff, o_offset;
 506        struct sk_buff *n_skb;
 507        int offset;
 508
 509        maxsize = skb->truesize;
 510
 511        diff = n_len - o_len;
 512        o_offset = o_buf - (char*) skb->data;
 513
 514        if (maxsize <= n_len) {
 515            if (diff != 0) {
 516                memcpy(skb->data + o_offset + n_len,o_buf + o_len,
 517                       skb->len - (o_offset + o_len));
 518            }
 519
 520            memcpy(skb->data + o_offset, n_buf, n_len);
 521
 522            n_skb    = skb;
 523            skb->len = n_len;
 524            skb->end = skb->head+n_len;
 525        } else {
 526                /*
 527                 *      Sizes differ, make a copy.
 528                 *
 529                 *      FIXME: move this to core/sbuff.c:skb_grow()
 530                 */
 531
 532                n_skb = alloc_skb(MAX_HEADER + skb->len + diff, pri);
 533                if (n_skb == NULL) {
 534                        IP_MASQ_ERR("skb_replace(): no room left (from %p)\n",
 535                               __builtin_return_address(0));
 536                        return skb;
 537
 538                }
 539                skb_reserve(n_skb, MAX_HEADER);
 540                skb_put(n_skb, skb->len + diff);
 541
 542                /*
 543                 *      Copy as much data from the old skb as possible. Even
 544                 *      though we're only forwarding packets, we need stuff
 545                 *      like skb->protocol (PPP driver wants it).
 546                 */
 547                offset = n_skb->data - skb->data;
 548                n_skb->nh.raw = skb->nh.raw + offset;
 549                n_skb->h.raw = skb->h.raw + offset;
 550                n_skb->dev = skb->dev;
 551                n_skb->mac.raw = skb->mac.raw + offset;
 552                n_skb->pkt_type = skb->pkt_type;
 553                n_skb->protocol = skb->protocol;
 554                n_skb->ip_summed = skb->ip_summed;
 555                n_skb->dst = dst_clone(skb->dst);
 556
 557                /*
 558                 * Copy pkt in new buffer
 559                 */
 560
 561                memcpy(n_skb->data, skb->data, o_offset);
 562                memcpy(n_skb->data + o_offset, n_buf, n_len);
 563                memcpy(n_skb->data + o_offset + n_len, o_buf + o_len,
 564                       skb->len - (o_offset + o_len) );
 565
 566                /*
 567                 * Problem, how to replace the new skb with old one,
 568                 * preferably inplace
 569                 */
 570
 571                kfree_skb(skb);
 572        }
 573        return n_skb;
 574}
 575
 576/*
 577 *      calls skb_replace() and update ip header if new skb was allocated
 578 */
 579
 580struct sk_buff * ip_masq_skb_replace(struct sk_buff *skb, int pri, char *o_buf, int o_len, char *n_buf, int n_len)
 581{
 582        int diff;
 583        struct sk_buff *n_skb;
 584        unsigned skb_len;
 585
 586        diff = n_len - o_len;
 587        n_skb = skb_replace(skb, pri, o_buf, o_len, n_buf, n_len);
 588        skb_len = skb->len;
 589
 590        if (diff)
 591        {
 592                struct iphdr *iph;
 593                IP_MASQ_DEBUG(1, "masq_skb_replace(): pkt resized for %d bytes (len=%d)\n", diff, skb->len);
 594                /*
 595                 *      update ip header
 596                 */
 597                iph = n_skb->nh.iph;
 598                iph->check = 0;
 599                iph->check = ip_fast_csum((unsigned char *)iph, iph->ihl);
 600                iph->tot_len = htons(skb_len + diff);
 601        }
 602        return n_skb;
 603}
 604
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.