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