Darren Reed wrote:
> Hi,
>
> Since I saw your email, I sat down and worked on a TFTP proxy to fit
> in with IPFilter.
>
> To make it work, you can take one of two paths.
> 1) copy ip_tftp_pxy.c into the IPFilter source code tree, modify the
> ip_proxy.c
>   file to include it and recompile IPFilter;
> 2) if you're using 4.1.13 or later, you can compile it as a standalone
> LKM and
>   load it into IPFilter for use that way.
>
> Darren
>

Sorry, forgot to attach the .c file.

Darren

/*
 * Copyright (C) 2006 by Darren Reed.
 *
 * See the IPFILTER.LICENCE file for details on licencing.
 *
 */
#include <sys/types.h>
#include <sys/modctl.h>
#include <sys/ddi.h>
#include <sys/uio.h>
#include <sys/sunddi.h>
#include <net/if.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/udp.h>
#include <netinet/tcp.h>
#include <netinet/ip_compat.h>
#include <netinet/ip_fil.h>
#include <netinet/ip_nat.h>
#include <netinet/ip_state.h>
#include <netinet/ip_proxy.h>

static int ipprtftpattach __P((dev_info_t *dip, ddi_attach_cmd_t cmd));
static int ipprtftpdetach __P((dev_info_t *dip, ddi_detach_cmd_t cmd));
static void ippr_tftp_fini __P((void));
static int ippr_tftp_new __P((fr_info_t *, ap_session_t *, nat_t *));
static int ippr_tftp_out __P((fr_info_t *, ap_session_t *, nat_t *));
static int ippr_tftp_in __P((fr_info_t *, ap_session_t *, nat_t *));
static int ippr_tftp_client __P((fr_info_t *, ap_session_t *, nat_t *));
static int ippr_tftp_server __P((fr_info_t *, ap_session_t *, nat_t *));

static int ippr_tftp_init __P((void));
static  frentry_t       tftpfr;

static aproxy_t tftp_proxy = {
        NULL,
        "tftp",
        (char)IPPROTO_UDP,
        0,
        0,
        ippr_tftp_init,
        ippr_tftp_fini,
        ippr_tftp_new,
        NULL,           /* del */
        ippr_tftp_in,
        ippr_tftp_out,
        NULL,           /* match */
        NULL            /* ctl */
};

int     tftp_proxy_init = 0;

typedef struct tftpinfo {
        nat_t   *ti_datanat;
        ipstate_t       *ti_datastate;
        int     ti_lastcmd;
        int     ti_nextblk;
        int     ti_lastblk;
        int     ti_lasterror;
        char    ti_filename[80];
} tftpinfo_t;

#define TFTP_CMD_READ   1
#define TFTP_CMD_WRITE  2
#define TFTP_CMD_DATA   3
#define TFTP_CMD_ACK    4
#define TFTP_CMD_ERROR  5


#ifdef STANDALONE
static struct dev_ops  drv_ops = {
        DEVO_REV,       /* devo_rev */
        0,              /* devo_refcnt */
        nodev,          /* devo_getinfo */
        nulldev,        /* devo_identify */
        nulldev,        /* devo_probe */
        ipprtftpattach, /* devo_attach */
        ipprtftpdetach, /* devo_detach */
        nodev,          /* devo_reset */
        NULL,           /* devo_cb_ops */
        NULL,           /* devo_bus_ops */
        NULL,           /* devo_power */
};

/*
 * Module linkage information for the kernel.
 */
static struct modldrv modldrv = {
        &mod_miscops,
        "IPFilter TFTP Proxy",
        &drv_ops        /* driver ops */
};

static struct modlinkage modlinkage = {
     MODREV_1,
     &modldrv,
     NULL
};



int _init()
{

        if (mod_install(&modlinkage) != DDI_SUCCESS) {
                return DDI_FAILURE;
        }

        if (appr_add(&tftp_proxy) != 0) {
                printf("appr_add failed\n");
                return DDI_FAILURE;
        }

        return DDI_SUCCESS;
}


int _fini()
{
        if (appr_del(&tftp_proxy) != 0) {
                return DDI_FAILURE;
        }

        if (mod_remove(&modlinkage) != DDI_SUCCESS)
                return DDI_FAILURE;

        return DDI_SUCCESS;
}


int _info(struct modinfo *modinfop)
{
        return (mod_info(&modlinkage, modinfop));
}


static int ipprtftpattach(dip, cmd)
dev_info_t *dip;
ddi_attach_cmd_t cmd;
{

        printf("ipprtftpattach(%p,%x)\n", dip, cmd);
        if (cmd != DDI_ATTACH)
                return DDI_FAILURE;

        if (appr_add(&tftp_proxy) != 0)
                return DDI_FAILURE;

        return DDI_SUCCESS;
}


static int ipprtftpdetach(dip, cmd)
dev_info_t *dip;
ddi_detach_cmd_t cmd;
{

        if (cmd != DDI_DETACH)
                return DDI_FAILURE;

        if (appr_del(&tftp_proxy) != 0)
                return DDI_FAILURE;

        return DDI_SUCCESS;
}

#endif /* STANDALONE */


/*
 * RCMD application proxy initialization.
 */
int ippr_tftp_init()
{

        bzero((char *)&tftpfr, sizeof(tftpfr));
        tftpfr.fr_ref = 1;
        tftpfr.fr_flags = FR_INQUE|FR_PASS|FR_QUICK|FR_KEEPSTATE;
        MUTEX_INIT(&tftpfr.fr_lock, "TFTP proxy rule lock");
        tftp_proxy_init = 1;

        return 0;
}


void ippr_tftp_fini()
{

        if (tftp_proxy_init == 1) {
                MUTEX_DESTROY(&tftpfr.fr_lock);
                tftp_proxy_init = 0;
        }
}


int ippr_tftp_out(fin, aps, nat)
fr_info_t *fin;
ap_session_t *aps;
nat_t *nat;
{

        if (nat->nat_dir == NAT_OUTBOUND)
                return ippr_tftp_client(fin, aps, nat);
        return ippr_tftp_server(fin, aps, nat);
}


int ippr_tftp_in(fin, aps, nat)
fr_info_t *fin;
ap_session_t *aps;
nat_t *nat;
{

        if (nat->nat_dir == NAT_INBOUND)
                return ippr_tftp_client(fin, aps, nat);
        return ippr_tftp_server(fin, aps, nat);
}


int ippr_tftp_new(fin, aps, nat)
fr_info_t *fin;
ap_session_t *aps;
nat_t *nat;
{
        udphdr_t *udp;
        tftpinfo_t *ti;

        KMALLOC(ti, tftpinfo_t *);
        if (ti == NULL)
                return -1;

        aps->aps_data = ti;
        aps->aps_psiz = sizeof(*ti);
        ti->ti_lastcmd = 0;

        nat = nat;      /* LINT */
        fin = fin;      /* LINT */

        udp = (udphdr_t *)fin->fin_dp;
        aps->aps_sport = udp->uh_sport;
        aps->aps_dport = udp->uh_dport;
        return 0;
}


/*
 * Setup for a new RCMD proxy.
 */
int ippr_tftp_backchannel(fin, aps, nat)
fr_info_t *fin;
ap_session_t *aps;
nat_t *nat;
{
        tftpinfo_t *ti;
        udphdr_t *udp;
        fr_info_t fi;
        u_short slen;
        nat_t *nat2;

        ti = aps->aps_data;
        udp = (udphdr_t *)fin->fin_dp;
        /*
         * Add skeleton NAT entry for connection which will come back the
         * other way.
         */
        bcopy((char *)fin, (char *)&fi, sizeof(fi));
        fi.fin_state = NULL;
        fi.fin_nat = NULL;
        fi.fin_flx |= FI_IGNORE;
        fi.fin_data[1] = 0;
        if (nat->nat_dir == NAT_OUTBOUND)
                nat2 = nat_outlookup(&fi, NAT_SEARCH|IPN_UDP, nat->nat_p,
                                     nat->nat_inip, nat->nat_oip);
        else
                nat2 = nat_inlookup(&fi, NAT_SEARCH|IPN_UDP, nat->nat_p,
                                    nat->nat_inip, nat->nat_oip);
        if (nat2 == NULL) {
                struct in_addr swip,swip2;
                int slen, nflags;
                ip_t *ip;

                ip = fin->fin_ip;
                slen = ip->ip_len;
                ip->ip_len = fin->fin_hlen + sizeof(*udp);
                bzero((char *)udp, sizeof(*udp));
                udp->uh_sport = htons(fi.fin_data[0]);
                udp->uh_dport = 0; /* XXX - don't specify remote port */
                udp->uh_ulen = 0;
                udp->uh_sum = 0;
                fi.fin_dp = (char *)udp;
                fi.fin_fr = &tftpfr;
                fi.fin_dlen = sizeof(*udp);
                fi.fin_plen = fi.fin_hlen + sizeof(*udp);
                fi.fin_flx &= FI_LOWTTL|FI_FRAG|FI_TCPUDP|FI_OPTIONS|FI_IGNORE;
                nflags = NAT_SLAVE|IPN_UDP|SI_W_DPORT;

                swip = ip->ip_src;
                swip2 = ip->ip_dst;

                if (nat->nat_dir == NAT_OUTBOUND) {
                        fi.fin_fi.fi_saddr = nat->nat_inip.s_addr;
                        ip->ip_src = nat->nat_inip;
                } else {
                        fi.fin_fi.fi_saddr = nat->nat_oip.s_addr;
                        ip->ip_src = nat->nat_oip;
                        nflags |= NAT_NOTRULEPORT;
                }

                nat2 = nat_new(&fi, nat->nat_ptr, NULL, nflags, nat->nat_dir);

                if (nat2 != NULL) {
                        (void) nat_proto(&fi, nat2, IPN_UDP);
                        nat_update(&fi, nat2, nat2->nat_ptr);
                        fi.fin_ifp = NULL;
                        if (nat->nat_dir == NAT_INBOUND) {
                                fi.fin_fi.fi_daddr = nat->nat_inip.s_addr;
                                ip->ip_dst = nat->nat_inip;
                        }
                        (void) fr_addstate(&fi, &nat2->nat_state, SI_W_DPORT);
                        if (fi.fin_state != NULL)
                                fr_statederef(&fi, (ipstate_t **)&fi.fin_state);
                        ti->ti_datanat = nat2;
                        ti->ti_datastate = fi.fin_state;
                }
                ip->ip_src = swip;
                ip->ip_dst = swip2;
                return 0;
        }
        return -1;
}


int ippr_tftp_client(fin, aps, nat)
fr_info_t *fin;
ap_session_t *aps;
nat_t *nat;
{
        u_char *msg, *s, *t;
        tftpinfo_t *ti;
        u_short opcode;
        udphdr_t *udp;
        int len;

        if (fin->fin_dlen < 4)
                return 0;

        ti = aps->aps_data;
        msg = fin->fin_dp;
        msg += sizeof(udphdr_t);
        opcode = (msg[0] << 8) | msg[1];

        switch (opcode)
        {
        case TFTP_CMD_READ :
        case TFTP_CMD_WRITE :
                if (fin->fin_out != 0)
                        return -1;
                len = fin->fin_dlen - sizeof(*udp) - 2;
                if (len > sizeof(ti->ti_filename) - 1)
                        len = sizeof(ti->ti_filename) - 1;
                s = msg + 2;
                for (t = (u_char *)ti->ti_filename; (len > 0); len--, s++) {
                        *t++ = *s;
                        if (*s == '\0')
                                break;
                }
                break;
        default :
                return -1;
        }

        ti = aps->aps_data;
        ti->ti_lastcmd = opcode;
        return 0;
}


int ippr_tftp_server(fin, aps, nat)
fr_info_t *fin;
ap_session_t *aps;
nat_t *nat;
{
        tftpinfo_t *ti;
        u_short opcode;
        u_short arg;
        u_char *msg;

        if (fin->fin_dlen < 4)
                return 0;

        msg = fin->fin_dp;
        msg += sizeof(udphdr_t);
        opcode = (msg[0] << 8) | msg[1];

        switch (opcode)
        {
        case TFTP_CMD_ACK :
                /* This proxy should not see any ACKS for DATA blocks */
                if (fin->fin_out != 1)
                        return -1;
                arg = (msg[2] << 8) | msg[3];
                if ((arg == 0) &&
                    (ti->ti_lastcmd == TFTP_CMD_READ ||
                     ti->ti_lastcmd == TFTP_CMD_WRITE))
                        ippr_tftp_backchannel(fin, aps, nat);
                ti->ti_lastblk = arg;
                break;
        case TFTP_CMD_ERROR :
                if (fin->fin_out != 1)
                        return -1;
                ti->ti_lasterror = arg;
                break;
        default :
                return -1;
        }

        ti = aps->aps_data;
        ti->ti_lastcmd = opcode;
        return 0;
}

Reply via email to