Home | History | Annotate | Line # | Download | only in netinet
ip_tftp_pxy.c revision 1.3
      1 /*	$NetBSD: ip_tftp_pxy.c,v 1.3 2012/07/22 14:27:51 darrenr Exp $	*/
      2 
      3 /*
      4  * Copyright (C) 2012 by Darren Reed.
      5  *
      6  * See the IPFILTER.LICENCE file for details on licencing.
      7  *
      8  * Id: ip_tftp_pxy.c,v 1.1.1.2 2012/07/22 13:45:38 darrenr Exp
      9  */
     10 
     11 #define IPF_TFTP_PROXY
     12 
     13 typedef struct ipf_tftp_softc_s {
     14         int     	ipf_p_tftp_readonly;
     15 	ipftuneable_t	*ipf_p_tftp_tune;
     16 } ipf_tftp_softc_t;
     17 
     18 int ipf_p_tftp_backchannel(fr_info_t *, ap_session_t *, nat_t *);
     19 int ipf_p_tftp_client(ipf_tftp_softc_t *, fr_info_t *, ap_session_t *,
     20 			   nat_t *);
     21 int ipf_p_tftp_in(void *, fr_info_t *, ap_session_t *, nat_t *);
     22 void ipf_p_tftp_main_load(void);
     23 void ipf_p_tftp_main_unload(void);
     24 int ipf_p_tftp_new(void *, fr_info_t *, ap_session_t *, nat_t *);
     25 void ipf_p_tftp_del(ipf_main_softc_t *, ap_session_t *);
     26 int ipf_p_tftp_out(void *, fr_info_t *, ap_session_t *, nat_t *);
     27 int ipf_p_tftp_server(ipf_tftp_softc_t *, fr_info_t *, ap_session_t *,
     28 			   nat_t *);
     29 void *ipf_p_tftp_soft_create(ipf_main_softc_t *);
     30 void ipf_p_tftp_soft_destroy(ipf_main_softc_t *, void *);
     31 
     32 static	frentry_t	tftpfr;
     33 static	int		tftp_proxy_init = 0;
     34 
     35 typedef enum tftp_cmd_e {
     36 	TFTP_CMD_READ = 1,
     37 	TFTP_CMD_WRITE = 2,
     38 	TFTP_CMD_DATA = 3,
     39 	TFTP_CMD_ACK = 4,
     40 	TFTP_CMD_ERROR = 5
     41 } tftp_cmd_t;
     42 
     43 typedef struct tftpinfo {
     44 	tftp_cmd_t	ti_lastcmd;
     45 	int		ti_nextblk;
     46 	int		ti_lastblk;
     47 	int		ti_lasterror;
     48 	char		ti_filename[80];
     49 	ipnat_t		*ti_rule;
     50 } tftpinfo_t;
     51 
     52 static  ipftuneable_t   ipf_tftp_tuneables[] = {
     53 	{ { (void *)offsetof(ipf_tftp_softc_t, ipf_p_tftp_readonly) },
     54 		"tftp_read_only",	0,	1,
     55 		stsizeof(ipf_tftp_softc_t, ipf_p_tftp_readonly),
     56 		0, NULL, NULL },
     57 	{ { NULL }, NULL, 0, 0, 0, 0, NULL, NULL }
     58 };
     59 
     60 
     61 /*
     62  * TFTP application proxy initialization.
     63  */
     64 void
     65 ipf_p_tftp_main_load(void)
     66 {
     67 
     68 	bzero((char *)&tftpfr, sizeof(tftpfr));
     69 	tftpfr.fr_ref = 1;
     70 	tftpfr.fr_flags = FR_INQUE|FR_PASS|FR_QUICK|FR_KEEPSTATE;
     71 	MUTEX_INIT(&tftpfr.fr_lock, "TFTP proxy rule lock");
     72 	tftp_proxy_init = 1;
     73 }
     74 
     75 
     76 void
     77 ipf_p_tftp_main_unload(void)
     78 {
     79 
     80 	if (tftp_proxy_init == 1) {
     81 		MUTEX_DESTROY(&tftpfr.fr_lock);
     82 		tftp_proxy_init = 0;
     83 	}
     84 }
     85 
     86 
     87 void *
     88 ipf_p_tftp_soft_create(softc)
     89 	ipf_main_softc_t *softc;
     90 {
     91 	ipf_tftp_softc_t *softt;
     92 
     93 	KMALLOC(softt, ipf_tftp_softc_t *);
     94 	if (softt == NULL)
     95 		return NULL;
     96 
     97 	bzero((char *)softt, sizeof(*softt));
     98 
     99 	softt->ipf_p_tftp_tune = ipf_tune_array_copy(softt,
    100 						     sizeof(ipf_tftp_tuneables),
    101 						     ipf_tftp_tuneables);
    102 	if (softt->ipf_p_tftp_tune == NULL) {
    103 		ipf_p_tftp_soft_destroy(softc, softt);
    104 		return NULL;
    105 	}
    106 	if (ipf_tune_array_link(softc, softt->ipf_p_tftp_tune) == -1) {
    107 		ipf_p_tftp_soft_destroy(softc, softt);
    108 		return NULL;
    109 	}
    110 
    111 	softt->ipf_p_tftp_readonly = 1;
    112 
    113 	return softt;
    114 }
    115 
    116 
    117 void
    118 ipf_p_tftp_soft_destroy(softc, arg)
    119         ipf_main_softc_t *softc;
    120         void *arg;
    121 {
    122 	ipf_tftp_softc_t *softt = arg;
    123 
    124 	if (softt->ipf_p_tftp_tune != NULL) {
    125 		ipf_tune_array_unlink(softc, softt->ipf_p_tftp_tune);
    126 		KFREES(softt->ipf_p_tftp_tune, sizeof(ipf_tftp_tuneables));
    127 		softt->ipf_p_tftp_tune = NULL;
    128 	}
    129 
    130 	KFREE(softt);
    131 }
    132 
    133 
    134 int
    135 ipf_p_tftp_out(void *arg, fr_info_t *fin, ap_session_t *aps, nat_t *nat)
    136 {
    137 	ipf_tftp_softc_t *softt = arg;
    138 
    139 	fin->fin_flx |= FI_NOWILD;
    140 	if (nat->nat_dir == NAT_OUTBOUND)
    141 		return ipf_p_tftp_client(softt, fin, aps, nat);
    142 	return ipf_p_tftp_server(softt, fin, aps, nat);
    143 }
    144 
    145 
    146 int
    147 ipf_p_tftp_in(void *arg, fr_info_t *fin, ap_session_t *aps, nat_t *nat)
    148 {
    149 	ipf_tftp_softc_t *softt = arg;
    150 
    151 	fin->fin_flx |= FI_NOWILD;
    152 	if (nat->nat_dir == NAT_INBOUND)
    153 		return ipf_p_tftp_client(softt, fin, aps, nat);
    154 	return ipf_p_tftp_server(softt, fin, aps, nat);
    155 }
    156 
    157 
    158 int
    159 ipf_p_tftp_new(void *arg, fr_info_t *fin, ap_session_t *aps, nat_t *nat)
    160 {
    161 	udphdr_t *udp;
    162 	tftpinfo_t *ti;
    163 	ipnat_t *ipn;
    164 	ipnat_t *np;
    165 	int size;
    166 
    167 	fin = fin;	/* LINT */
    168 
    169 	np = nat->nat_ptr;
    170 	size = np->in_size;
    171 
    172 	KMALLOC(ti, tftpinfo_t *);
    173 	if (ti == NULL)
    174 		return -1;
    175 	KMALLOCS(ipn, ipnat_t *, size);
    176 	if (ipn == NULL) {
    177 		KFREE(ti);
    178 		return -1;
    179 	}
    180 
    181 	aps->aps_data = ti;
    182 	aps->aps_psiz = sizeof(*ti);
    183 	bzero((char *)ti, sizeof(*ti));
    184 	bzero((char *)ipn, size);
    185 	ti->ti_rule = ipn;
    186 
    187 	udp = (udphdr_t *)fin->fin_dp;
    188 	aps->aps_sport = udp->uh_sport;
    189 	aps->aps_dport = udp->uh_dport;
    190 
    191 	ipn->in_size = size;
    192 	ipn->in_apr = NULL;
    193 	ipn->in_use = 1;
    194 	ipn->in_hits = 1;
    195 	ipn->in_ippip = 1;
    196 	ipn->in_pr[0] = IPPROTO_UDP;
    197 	ipn->in_pr[1] = IPPROTO_UDP;
    198 	ipn->in_ifps[0] = nat->nat_ifps[0];
    199 	ipn->in_ifps[1] = nat->nat_ifps[1];
    200 	ipn->in_v[0] = nat->nat_ptr->in_v[1];
    201 	ipn->in_v[1] = nat->nat_ptr->in_v[0];
    202 	ipn->in_flags = IPN_UDP|IPN_FIXEDDPORT|IPN_PROXYRULE;
    203 
    204 	ipn->in_nsrcip6 = nat->nat_odst6;
    205 	ipn->in_osrcip6 = nat->nat_ndst6;
    206 
    207 	if ((np->in_redir & NAT_REDIRECT) != 0) {
    208 		ipn->in_redir = NAT_MAP;
    209 		if (ipn->in_v[0] == 4) {
    210 			ipn->in_snip = ntohl(nat->nat_odstaddr);
    211 			ipn->in_dnip = ntohl(nat->nat_nsrcaddr);
    212 		} else {
    213 #ifdef USE_INET6
    214 			ipn->in_snip6 = nat->nat_odst6;
    215 			ipn->in_dnip6 = nat->nat_nsrc6;
    216 #endif
    217 		}
    218 		ipn->in_ndstip6 = nat->nat_nsrc6;
    219 		ipn->in_odstip6 = nat->nat_osrc6;
    220 	} else {
    221 		ipn->in_redir = NAT_REDIRECT;
    222 		if (ipn->in_v[0] == 4) {
    223 			ipn->in_snip = ntohl(nat->nat_odstaddr);
    224 			ipn->in_dnip = ntohl(nat->nat_osrcaddr);
    225 		} else {
    226 #ifdef USE_INET6
    227 			ipn->in_snip6 = nat->nat_odst6;
    228 			ipn->in_dnip6 = nat->nat_osrc6;
    229 #endif
    230 		}
    231 		ipn->in_ndstip6 = nat->nat_osrc6;
    232 		ipn->in_odstip6 = nat->nat_nsrc6;
    233 	}
    234 	ipn->in_odport = htons(fin->fin_sport);
    235 	ipn->in_ndport = htons(fin->fin_sport);
    236 
    237 	IP6_SETONES(&ipn->in_osrcmsk6);
    238 	IP6_SETONES(&ipn->in_nsrcmsk6);
    239 	IP6_SETONES(&ipn->in_odstmsk6);
    240 	IP6_SETONES(&ipn->in_ndstmsk6);
    241 	MUTEX_INIT(&ipn->in_lock, "tftp proxy NAT rule");
    242 
    243 	ipn->in_namelen = np->in_namelen;
    244 	bcopy(np->in_names, ipn->in_ifnames, ipn->in_namelen);
    245 	ipn->in_ifnames[0] = np->in_ifnames[0];
    246 	ipn->in_ifnames[1] = np->in_ifnames[1];
    247 
    248 	ti->ti_lastcmd = 0;
    249 
    250 	return 0;
    251 }
    252 
    253 
    254 void
    255 ipf_p_tftp_del(softc, aps)
    256 	ipf_main_softc_t *softc;
    257 	ap_session_t *aps;
    258 {
    259 	tftpinfo_t *tftp;
    260 
    261 	tftp = aps->aps_data;
    262 	if (tftp != NULL) {
    263 		tftp->ti_rule->in_flags |= IPN_DELETE;
    264 		ipf_nat_rule_deref(softc, &tftp->ti_rule);
    265 	}
    266 }
    267 
    268 
    269 /*
    270  * Setup for a new TFTP proxy.
    271  */
    272 int
    273 ipf_p_tftp_backchannel(fr_info_t *fin, ap_session_t *aps, nat_t *nat)
    274 {
    275 	ipf_main_softc_t *softc = fin->fin_main_soft;
    276 #ifdef USE_MUTEXES
    277 	ipf_nat_softc_t *softn = softc->ipf_nat_soft;
    278 #endif
    279 #ifdef USE_INET6
    280 	i6addr_t swip6, sw2ip6;
    281 	ip6_t *ip6;
    282 #endif
    283 	struct in_addr swip, sw2ip;
    284 	tftpinfo_t *ti;
    285 	udphdr_t udp;
    286 	fr_info_t fi;
    287 	u_short slen;
    288 	nat_t *nat2;
    289 	int nflags;
    290 	ip_t *ip;
    291 	int dir;
    292 
    293 	ti = aps->aps_data;
    294 	/*
    295 	 * Add skeleton NAT entry for connection which will come back the
    296 	 * other way.
    297 	 */
    298 	bcopy((char *)fin, (char *)&fi, sizeof(fi));
    299 	fi.fin_flx |= FI_IGNORE;
    300 	fi.fin_data[1] = 0;
    301 
    302 	bzero((char *)&udp, sizeof(udp));
    303 	udp.uh_sport = 0;	/* XXX - don't specify remote port */
    304 	udp.uh_dport = ti->ti_rule->in_ndport;
    305 	udp.uh_ulen = htons(sizeof(udp));
    306 	udp.uh_sum = 0;
    307 
    308 	fi.fin_fr = &tftpfr;
    309 	fi.fin_dp = (char *)&udp;
    310 	fi.fin_sport = 0;
    311 	fi.fin_dport = ntohs(ti->ti_rule->in_ndport);
    312 	fi.fin_dlen = sizeof(udp);
    313 	fi.fin_plen = fi.fin_hlen + sizeof(udp);
    314 	fi.fin_flx &= FI_LOWTTL|FI_FRAG|FI_TCPUDP|FI_OPTIONS|FI_IGNORE;
    315 	nflags = NAT_SLAVE|IPN_UDP|SI_W_SPORT;
    316 #ifdef USE_INET6
    317 	ip6 = (ip6_t *)fin->fin_ip;
    318 #endif
    319 	ip = fin->fin_ip;
    320 	sw2ip.s_addr = 0;
    321 	swip.s_addr = 0;
    322 
    323 	fi.fin_src6 = nat->nat_ndst6;
    324 	fi.fin_dst6 = nat->nat_nsrc6;
    325 	if (nat->nat_v[0] == 4) {
    326 		slen = ip->ip_len;
    327 		ip->ip_len = htons(fin->fin_hlen + sizeof(udp));
    328 		swip = ip->ip_src;
    329 		sw2ip = ip->ip_dst;
    330 		ip->ip_src = nat->nat_ndstip;
    331 		ip->ip_dst = nat->nat_nsrcip;
    332 	} else {
    333 #ifdef USE_INET6
    334 		slen = ip6->ip6_plen;
    335 		ip6->ip6_plen = htons(sizeof(udp));
    336 		swip6.in6 = ip6->ip6_src;
    337 		sw2ip6.in6 = ip6->ip6_dst;
    338 		ip6->ip6_src = nat->nat_ndst6.in6;
    339 		ip6->ip6_dst = nat->nat_nsrc6.in6;
    340 #endif
    341 	}
    342 
    343 	if (nat->nat_dir == NAT_INBOUND) {
    344 		dir = NAT_OUTBOUND;
    345 		fi.fin_out = 1;
    346 	} else {
    347 		dir = NAT_INBOUND;
    348 		fi.fin_out = 0;
    349 	}
    350 	nflags |= NAT_NOTRULEPORT;
    351 
    352 	MUTEX_ENTER(&softn->ipf_nat_new);
    353 	if (nat->nat_v[0] == 4)
    354 		nat2 = ipf_nat_add(&fi, ti->ti_rule, NULL, nflags, dir);
    355 	else
    356 		nat2 = ipf_nat6_add(&fi, ti->ti_rule, NULL, nflags, dir);
    357 	MUTEX_EXIT(&softn->ipf_nat_new);
    358 	if (nat2 != NULL) {
    359 		(void) ipf_nat_proto(&fi, nat2, IPN_UDP);
    360 		ipf_nat_update(&fi, nat2);
    361 		fi.fin_ifp = NULL;
    362 		if (ti->ti_rule->in_redir == NAT_MAP) {
    363 			fi.fin_src6 = nat->nat_ndst6;
    364 			fi.fin_dst6 = nat->nat_nsrc6;
    365 			if (nat->nat_v[0] == 4) {
    366 				ip->ip_src = nat->nat_ndstip;
    367 				ip->ip_dst = nat->nat_nsrcip;
    368 			} else {
    369 #ifdef USE_INET6
    370 				ip6->ip6_src = nat->nat_ndst6.in6;
    371 				ip6->ip6_dst = nat->nat_nsrc6.in6;
    372 #endif
    373 			}
    374 		} else {
    375 			fi.fin_src6 = nat->nat_odst6;
    376 			fi.fin_dst6 = nat->nat_osrc6;
    377 			if (fin->fin_v == 4) {
    378 				ip->ip_src = nat->nat_odstip;
    379 				ip->ip_dst = nat->nat_osrcip;
    380 			} else {
    381 #ifdef USE_INET6
    382 				ip6->ip6_src = nat->nat_odst6.in6;
    383 				ip6->ip6_dst = nat->nat_osrc6.in6;
    384 #endif
    385 			}
    386 		}
    387 		if (ipf_state_add(softc, &fi, NULL, SI_W_SPORT) != 0) {
    388 			ipf_nat_setpending(softc, nat2);
    389 		}
    390 	}
    391 	if (nat->nat_v[0] == 4) {
    392 		ip->ip_len = slen;
    393 		ip->ip_src = swip;
    394 		ip->ip_dst = sw2ip;
    395 	} else {
    396 #ifdef USE_INET6
    397 		ip6->ip6_plen = slen;
    398 		ip6->ip6_src = swip6.in6;
    399 		ip6->ip6_dst = sw2ip6.in6;
    400 #endif
    401 	}
    402 	return 0;
    403 }
    404 
    405 
    406 int
    407 ipf_p_tftp_client(ipf_tftp_softc_t *softt, fr_info_t *fin, ap_session_t *aps,
    408 	nat_t *nat)
    409 {
    410 	u_char *msg, *s, *t;
    411 	tftpinfo_t *ti;
    412 	u_short opcode;
    413 	udphdr_t *udp;
    414 	int len;
    415 
    416 	if (fin->fin_dlen < 4)
    417 		return 0;
    418 
    419 	ti = aps->aps_data;
    420 	msg = fin->fin_dp;
    421 	msg += sizeof(udphdr_t);
    422 	opcode = (msg[0] << 8) | msg[1];
    423 	DT3(tftp_cmd, fr_info_t *, fin, int, opcode, nat_t *, nat);
    424 
    425 	switch (opcode)
    426 	{
    427 	case TFTP_CMD_WRITE :
    428 		if (softt->ipf_p_tftp_readonly != 0)
    429 			break;
    430 		/* FALLTHROUGH */
    431 	case TFTP_CMD_READ :
    432 		len = fin->fin_dlen - sizeof(*udp) - 2;
    433 		if (len > sizeof(ti->ti_filename) - 1)
    434 			len = sizeof(ti->ti_filename) - 1;
    435 		s = msg + 2;
    436 		for (t = (u_char *)ti->ti_filename; (len > 0); len--, s++) {
    437 			*t++ = *s;
    438 			if (*s == '\0')
    439 				break;
    440 		}
    441 		ipf_p_tftp_backchannel(fin, aps, nat);
    442 		break;
    443 	default :
    444 		return -1;
    445 	}
    446 
    447 	ti = aps->aps_data;
    448 	ti->ti_lastcmd = opcode;
    449 	return 0;
    450 }
    451 
    452 
    453 int
    454 ipf_p_tftp_server(ipf_tftp_softc_t *softt, fr_info_t *fin, ap_session_t *aps,
    455 	nat_t *nat)
    456 {
    457 	tftpinfo_t *ti;
    458 	u_short opcode;
    459 	u_short arg;
    460 	u_char *msg;
    461 
    462 	if (fin->fin_dlen < 4)
    463 		return 0;
    464 
    465 	ti = aps->aps_data;
    466 	msg = fin->fin_dp;
    467 	msg += sizeof(udphdr_t);
    468 	arg = (msg[2] << 8) | msg[3];
    469 	opcode = (msg[0] << 8) | msg[1];
    470 
    471 	switch (opcode)
    472 	{
    473 	case TFTP_CMD_ACK :
    474 		ti->ti_lastblk = arg;
    475 		break;
    476 
    477 	case TFTP_CMD_ERROR :
    478 		ti->ti_lasterror = arg;
    479 		break;
    480 
    481 	default :
    482 		return -1;
    483 	}
    484 
    485 	ti->ti_lastcmd = opcode;
    486 	return 0;
    487 }
    488