]> CyberLeo.Net >> Repos - FreeBSD/releng/10.0.git/blob - sys/contrib/ipfilter/netinet/ip_tftp_pxy.c
- Copy stable/10 (r259064) to releng/10.0 as part of the
[FreeBSD/releng/10.0.git] / sys / contrib / ipfilter / netinet / ip_tftp_pxy.c
1 /*
2  * Copyright (C) 2012 by Darren Reed.
3  *
4  * See the IPFILTER.LICENCE file for details on licencing.
5  *
6  * $Id: ip_tftp_pxy.c,v 1.1.2.9 2012/07/22 08:04:23 darren_r Exp $
7  */
8
9 #define IPF_TFTP_PROXY
10
11 typedef struct ipf_tftp_softc_s {
12         int             ipf_p_tftp_readonly;
13         ipftuneable_t   *ipf_p_tftp_tune;
14 } ipf_tftp_softc_t;
15
16 int ipf_p_tftp_backchannel __P((fr_info_t *, ap_session_t *, nat_t *));
17 int ipf_p_tftp_client __P((ipf_tftp_softc_t *, fr_info_t *, ap_session_t *,
18                            nat_t *));
19 int ipf_p_tftp_in __P((void *, fr_info_t *, ap_session_t *, nat_t *));
20 void ipf_p_tftp_main_load __P((void));
21 void ipf_p_tftp_main_unload __P((void));
22 int ipf_p_tftp_new __P((void *, fr_info_t *, ap_session_t *, nat_t *));
23 void ipf_p_tftp_del __P((ipf_main_softc_t *, ap_session_t *));
24 int ipf_p_tftp_out __P((void *, fr_info_t *, ap_session_t *, nat_t *));
25 int ipf_p_tftp_server __P((ipf_tftp_softc_t *, fr_info_t *, ap_session_t *,
26                            nat_t *));
27 void *ipf_p_tftp_soft_create __P((ipf_main_softc_t *));
28 void ipf_p_tftp_soft_destroy __P((ipf_main_softc_t *, void *));
29
30 static  frentry_t       tftpfr;
31 static  int             tftp_proxy_init = 0;
32
33 typedef enum tftp_cmd_e {
34         TFTP_CMD_READ = 1,
35         TFTP_CMD_WRITE = 2,
36         TFTP_CMD_DATA = 3,
37         TFTP_CMD_ACK = 4,
38         TFTP_CMD_ERROR = 5
39 } tftp_cmd_t;
40
41 typedef struct tftpinfo {
42         tftp_cmd_t      ti_lastcmd;
43         int             ti_nextblk;
44         int             ti_lastblk;
45         int             ti_lasterror;
46         char            ti_filename[80];
47         ipnat_t         *ti_rule;
48 } tftpinfo_t;
49
50 static  ipftuneable_t   ipf_tftp_tuneables[] = {
51         { { (void *)offsetof(ipf_tftp_softc_t, ipf_p_tftp_readonly) },
52                 "tftp_read_only",       0,      1,
53                 stsizeof(ipf_tftp_softc_t, ipf_p_tftp_readonly),
54                 0, NULL, NULL },
55         { { NULL }, NULL, 0, 0, 0, 0, NULL, NULL }
56 };
57
58
59 /*
60  * TFTP application proxy initialization.
61  */
62 void
63 ipf_p_tftp_main_load()
64 {
65
66         bzero((char *)&tftpfr, sizeof(tftpfr));
67         tftpfr.fr_ref = 1;
68         tftpfr.fr_flags = FR_INQUE|FR_PASS|FR_QUICK|FR_KEEPSTATE;
69         MUTEX_INIT(&tftpfr.fr_lock, "TFTP proxy rule lock");
70         tftp_proxy_init = 1;
71 }
72
73
74 void
75 ipf_p_tftp_main_unload()
76 {
77
78         if (tftp_proxy_init == 1) {
79                 MUTEX_DESTROY(&tftpfr.fr_lock);
80                 tftp_proxy_init = 0;
81         }
82 }
83
84
85 void *
86 ipf_p_tftp_soft_create(softc)
87         ipf_main_softc_t *softc;
88 {
89         ipf_tftp_softc_t *softt;
90
91         KMALLOC(softt, ipf_tftp_softc_t *);
92         if (softt == NULL)
93                 return NULL;
94
95         bzero((char *)softt, sizeof(*softt));
96
97         softt->ipf_p_tftp_tune = ipf_tune_array_copy(softt,
98                                                      sizeof(ipf_tftp_tuneables),
99                                                      ipf_tftp_tuneables);
100         if (softt->ipf_p_tftp_tune == NULL) {
101                 ipf_p_tftp_soft_destroy(softc, softt);
102                 return NULL;
103         }
104         if (ipf_tune_array_link(softc, softt->ipf_p_tftp_tune) == -1) {
105                 ipf_p_tftp_soft_destroy(softc, softt);
106                 return NULL;
107         }
108
109         softt->ipf_p_tftp_readonly = 1;
110
111         return softt;
112 }
113
114
115 void
116 ipf_p_tftp_soft_destroy(softc, arg)
117         ipf_main_softc_t *softc;
118         void *arg;
119 {
120         ipf_tftp_softc_t *softt = arg;
121
122         if (softt->ipf_p_tftp_tune != NULL) {
123                 ipf_tune_array_unlink(softc, softt->ipf_p_tftp_tune);
124                 KFREES(softt->ipf_p_tftp_tune, sizeof(ipf_tftp_tuneables));
125                 softt->ipf_p_tftp_tune = NULL;
126         }
127
128         KFREE(softt);
129 }
130
131
132 int
133 ipf_p_tftp_out(arg, fin, aps, nat)
134         void *arg;
135         fr_info_t *fin;
136         ap_session_t *aps;
137         nat_t *nat;
138 {
139         ipf_tftp_softc_t *softt = arg;
140
141         fin->fin_flx |= FI_NOWILD;
142         if (nat->nat_dir == NAT_OUTBOUND)
143                 return ipf_p_tftp_client(softt, fin, aps, nat);
144         return ipf_p_tftp_server(softt, fin, aps, nat);
145 }
146
147
148 int
149 ipf_p_tftp_in(arg, fin, aps, nat)
150         void *arg;
151         fr_info_t *fin;
152         ap_session_t *aps;
153         nat_t *nat;
154 {
155         ipf_tftp_softc_t *softt = arg;
156
157         fin->fin_flx |= FI_NOWILD;
158         if (nat->nat_dir == NAT_INBOUND)
159                 return ipf_p_tftp_client(softt, fin, aps, nat);
160         return ipf_p_tftp_server(softt, fin, aps, nat);
161 }
162
163
164 int
165 ipf_p_tftp_new(arg, fin, aps, nat)
166         void *arg;
167         fr_info_t *fin;
168         ap_session_t *aps;
169         nat_t *nat;
170 {
171         udphdr_t *udp;
172         tftpinfo_t *ti;
173         ipnat_t *ipn;
174         ipnat_t *np;
175         int size;
176
177         fin = fin;      /* LINT */
178
179         np = nat->nat_ptr;
180         size = np->in_size;
181
182         KMALLOC(ti, tftpinfo_t *);
183         if (ti == NULL)
184                 return -1;
185         KMALLOCS(ipn, ipnat_t *, size);
186         if (ipn == NULL) {
187                 KFREE(ti);
188                 return -1;
189         }
190
191         aps->aps_data = ti;
192         aps->aps_psiz = sizeof(*ti);
193         bzero((char *)ti, sizeof(*ti));
194         bzero((char *)ipn, size);
195         ti->ti_rule = ipn;
196
197         udp = (udphdr_t *)fin->fin_dp;
198         aps->aps_sport = udp->uh_sport;
199         aps->aps_dport = udp->uh_dport;
200
201         ipn->in_size = size;
202         ipn->in_apr = NULL;
203         ipn->in_use = 1;
204         ipn->in_hits = 1;
205         ipn->in_ippip = 1;
206         ipn->in_pr[0] = IPPROTO_UDP;
207         ipn->in_pr[1] = IPPROTO_UDP;
208         ipn->in_ifps[0] = nat->nat_ifps[0];
209         ipn->in_ifps[1] = nat->nat_ifps[1];
210         ipn->in_v[0] = nat->nat_ptr->in_v[1];
211         ipn->in_v[1] = nat->nat_ptr->in_v[0];
212         ipn->in_flags = IPN_UDP|IPN_FIXEDDPORT|IPN_PROXYRULE;
213
214         ipn->in_nsrcip6 = nat->nat_odst6;
215         ipn->in_osrcip6 = nat->nat_ndst6;
216
217         if ((np->in_redir & NAT_REDIRECT) != 0) {
218                 ipn->in_redir = NAT_MAP;
219                 if (ipn->in_v[0] == 4) {
220                         ipn->in_snip = ntohl(nat->nat_odstaddr);
221                         ipn->in_dnip = ntohl(nat->nat_nsrcaddr);
222                 } else {
223 #ifdef USE_INET6
224                         ipn->in_snip6 = nat->nat_odst6;
225                         ipn->in_dnip6 = nat->nat_nsrc6;
226 #endif
227                 }
228                 ipn->in_ndstip6 = nat->nat_nsrc6;
229                 ipn->in_odstip6 = nat->nat_osrc6;
230         } else {
231                 ipn->in_redir = NAT_REDIRECT;
232                 if (ipn->in_v[0] == 4) {
233                         ipn->in_snip = ntohl(nat->nat_odstaddr);
234                         ipn->in_dnip = ntohl(nat->nat_osrcaddr);
235                 } else {
236 #ifdef USE_INET6
237                         ipn->in_snip6 = nat->nat_odst6;
238                         ipn->in_dnip6 = nat->nat_osrc6;
239 #endif
240                 }
241                 ipn->in_ndstip6 = nat->nat_osrc6;
242                 ipn->in_odstip6 = nat->nat_nsrc6;
243         }
244         ipn->in_odport = htons(fin->fin_sport);
245         ipn->in_ndport = htons(fin->fin_sport);
246
247         IP6_SETONES(&ipn->in_osrcmsk6);
248         IP6_SETONES(&ipn->in_nsrcmsk6);
249         IP6_SETONES(&ipn->in_odstmsk6);
250         IP6_SETONES(&ipn->in_ndstmsk6);
251         MUTEX_INIT(&ipn->in_lock, "tftp proxy NAT rule");
252
253         ipn->in_namelen = np->in_namelen;
254         bcopy(np->in_names, ipn->in_ifnames, ipn->in_namelen);
255         ipn->in_ifnames[0] = np->in_ifnames[0];
256         ipn->in_ifnames[1] = np->in_ifnames[1];
257
258         ti->ti_lastcmd = 0;
259
260         return 0;
261 }
262
263
264 void
265 ipf_p_tftp_del(softc, aps)
266         ipf_main_softc_t *softc;
267         ap_session_t *aps;
268 {
269         tftpinfo_t *tftp;
270
271         tftp = aps->aps_data;
272         if (tftp != NULL) {
273                 tftp->ti_rule->in_flags |= IPN_DELETE;
274                 ipf_nat_rule_deref(softc, &tftp->ti_rule);
275         }
276 }
277
278
279 /*
280  * Setup for a new TFTP proxy.
281  */
282 int
283 ipf_p_tftp_backchannel(fin, aps, nat)
284         fr_info_t *fin;
285         ap_session_t *aps;
286         nat_t *nat;
287 {
288         ipf_main_softc_t *softc = fin->fin_main_soft;
289 #ifdef USE_MUTEXES
290         ipf_nat_softc_t *softn = softc->ipf_nat_soft;
291 #endif
292 #ifdef USE_INET6
293         i6addr_t swip6, sw2ip6;
294         ip6_t *ip6;
295 #endif
296         struct in_addr swip, sw2ip;
297         tftpinfo_t *ti;
298         udphdr_t udp;
299         fr_info_t fi;
300         u_short slen = 0; /* silence gcc */
301         nat_t *nat2;
302         int nflags;
303         ip_t *ip;
304         int dir;
305
306         ti = aps->aps_data;
307         /*
308          * Add skeleton NAT entry for connection which will come back the
309          * other way.
310          */
311         bcopy((char *)fin, (char *)&fi, sizeof(fi));
312         fi.fin_flx |= FI_IGNORE;
313         fi.fin_data[1] = 0;
314
315         bzero((char *)&udp, sizeof(udp));
316         udp.uh_sport = 0;       /* XXX - don't specify remote port */
317         udp.uh_dport = ti->ti_rule->in_ndport;
318         udp.uh_ulen = htons(sizeof(udp));
319         udp.uh_sum = 0;
320
321         fi.fin_fr = &tftpfr;
322         fi.fin_dp = (char *)&udp;
323         fi.fin_sport = 0;
324         fi.fin_dport = ntohs(ti->ti_rule->in_ndport);
325         fi.fin_dlen = sizeof(udp);
326         fi.fin_plen = fi.fin_hlen + sizeof(udp);
327         fi.fin_flx &= FI_LOWTTL|FI_FRAG|FI_TCPUDP|FI_OPTIONS|FI_IGNORE;
328         nflags = NAT_SLAVE|IPN_UDP|SI_W_SPORT;
329 #ifdef USE_INET6
330         ip6 = (ip6_t *)fin->fin_ip;
331 #endif
332         ip = fin->fin_ip;
333         sw2ip.s_addr = 0;
334         swip.s_addr = 0;
335
336         fi.fin_src6 = nat->nat_ndst6;
337         fi.fin_dst6 = nat->nat_nsrc6;
338         if (nat->nat_v[0] == 4) {
339                 slen = ip->ip_len;
340                 ip->ip_len = htons(fin->fin_hlen + sizeof(udp));
341                 swip = ip->ip_src;
342                 sw2ip = ip->ip_dst;
343                 ip->ip_src = nat->nat_ndstip;
344                 ip->ip_dst = nat->nat_nsrcip;
345         } else {
346 #ifdef USE_INET6
347                 slen = ip6->ip6_plen;
348                 ip6->ip6_plen = htons(sizeof(udp));
349                 swip6.in6 = ip6->ip6_src;
350                 sw2ip6.in6 = ip6->ip6_dst;
351                 ip6->ip6_src = nat->nat_ndst6.in6;
352                 ip6->ip6_dst = nat->nat_nsrc6.in6;
353 #endif
354         }
355
356         if (nat->nat_dir == NAT_INBOUND) {
357                 dir = NAT_OUTBOUND;
358                 fi.fin_out = 1;
359         } else {
360                 dir = NAT_INBOUND;
361                 fi.fin_out = 0;
362         }
363         nflags |= NAT_NOTRULEPORT;
364
365         MUTEX_ENTER(&softn->ipf_nat_new);
366 #ifdef USE_INET6
367         if (nat->nat_v[0] == 6)
368                 nat2 = ipf_nat6_add(&fi, ti->ti_rule, NULL, nflags, dir);
369         else
370 #endif
371                 nat2 = ipf_nat_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 }