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;
}