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