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