ip_proxy.c revision 60855
1/*
2 * Copyright (C) 1997-2000 by Darren Reed.
3 *
4 * Redistribution and use in source and binary forms are permitted
5 * provided that this notice is preserved and due credit is given
6 * to the original author and the contributors.
7 */
8#if !defined(lint)
9/*static const char rcsid[] = "@(#)$Id: ip_proxy.c,v 2.2.2.1 1999/09/19 12:18:19 darrenr Exp $";*/
10static const char rcsid[] = "@(#)$FreeBSD: head/sys/contrib/ipfilter/netinet/ip_proxy.c 60855 2000-05-24 04:21:35Z darrenr $";
11#endif
12
13#if defined(__FreeBSD__) && defined(KERNEL) && !defined(_KERNEL)
14# define	_KERNEL
15#endif
16
17#include <sys/errno.h>
18#include <sys/types.h>
19#include <sys/param.h>
20#include <sys/time.h>
21#include <sys/file.h>
22#if !defined(__FreeBSD_version)
23# include <sys/ioctl.h>
24#endif
25#include <sys/fcntl.h>
26#include <sys/uio.h>
27#if !defined(_KERNEL) && !defined(KERNEL)
28# include <stdio.h>
29# include <string.h>
30# include <stdlib.h>
31#endif
32#ifndef	linux
33# include <sys/protosw.h>
34#endif
35#include <sys/socket.h>
36#if defined(_KERNEL)
37# if !defined(linux)
38#  include <sys/systm.h>
39# else
40#  include <linux/string.h>
41# endif
42#endif
43#if !defined(__SVR4) && !defined(__svr4__)
44# ifndef linux
45#  include <sys/mbuf.h>
46# endif
47#else
48# include <sys/byteorder.h>
49# ifdef _KERNEL
50#  include <sys/dditypes.h>
51# endif
52# include <sys/stream.h>
53# include <sys/kmem.h>
54#endif
55#if __FreeBSD__ > 2
56# include <sys/queue.h>
57#endif
58#include <net/if.h>
59#ifdef sun
60# include <net/af.h>
61#endif
62#include <net/route.h>
63#include <netinet/in.h>
64#include <netinet/in_systm.h>
65#include <netinet/ip.h>
66#ifndef linux
67# include <netinet/ip_var.h>
68#endif
69#include <netinet/tcp.h>
70#include <netinet/udp.h>
71#include <netinet/ip_icmp.h>
72#include "netinet/ip_compat.h"
73#include <netinet/tcpip.h>
74#include "netinet/ip_fil.h"
75#include "netinet/ip_proxy.h"
76#include "netinet/ip_nat.h"
77#include "netinet/ip_state.h"
78#if (__FreeBSD_version >= 300000)
79# include <sys/malloc.h>
80#endif
81
82
83#ifndef MIN
84#define MIN(a,b)        (((a)<(b))?(a):(b))
85#endif
86
87static ap_session_t *appr_new_session __P((aproxy_t *, ip_t *,
88					   fr_info_t *, nat_t *));
89static int appr_fixseqack __P((fr_info_t *, ip_t *, ap_session_t *, int ));
90
91
92#define	AP_SESS_SIZE	53
93
94#if defined(_KERNEL) && !defined(linux)
95#include "netinet/ip_ftp_pxy.c"
96#include "netinet/ip_rcmd_pxy.c"
97#include "netinet/ip_raudio_pxy.c"
98#endif
99
100ap_session_t	*ap_sess_tab[AP_SESS_SIZE];
101ap_session_t	*ap_sess_list = NULL;
102aproxy_t	*ap_proxylist = NULL;
103aproxy_t	ap_proxies[] = {
104#ifdef	IPF_FTP_PROXY
105	{ NULL, "ftp", (char)IPPROTO_TCP, 0, 0, ippr_ftp_init, NULL,
106	  ippr_ftp_new, ippr_ftp_in, ippr_ftp_out },
107#endif
108#ifdef	IPF_RCMD_PROXY
109	{ NULL, "rcmd", (char)IPPROTO_TCP, 0, 0, ippr_rcmd_init, NULL,
110	  ippr_rcmd_new, NULL, ippr_rcmd_out },
111#endif
112#ifdef	IPF_RAUDIO_PROXY
113	{ NULL, "raudio", (char)IPPROTO_TCP, 0, 0, ippr_raudio_init, NULL,
114	  ippr_raudio_new, ippr_raudio_in, ippr_raudio_out },
115#endif
116	{ NULL, "", '\0', 0, 0, NULL, NULL }
117};
118
119
120int appr_add(ap)
121aproxy_t *ap;
122{
123	aproxy_t *a;
124
125	for (a = ap_proxies; a->apr_p; a++)
126		if ((a->apr_p == ap->apr_p) &&
127		    !strncmp(a->apr_label, ap->apr_label,
128			     sizeof(ap->apr_label)))
129			return -1;
130
131	for (a = ap_proxylist; a->apr_p; a = a->apr_next)
132		if ((a->apr_p == ap->apr_p) &&
133		    !strncmp(a->apr_label, ap->apr_label,
134			     sizeof(ap->apr_label)))
135			return -1;
136	ap->apr_next = ap_proxylist;
137	ap_proxylist = ap;
138	return (*ap->apr_init)();
139}
140
141
142int appr_del(ap)
143aproxy_t *ap;
144{
145	aproxy_t *a, **app;
146
147	for (app = &ap_proxylist; (a = *app); app = &a->apr_next)
148		if (a == ap) {
149			if (ap->apr_ref != 0)
150				return 1;
151			*app = a->apr_next;
152			return 0;
153		}
154	return -1;
155}
156
157
158int appr_ok(ip, tcp, nat)
159ip_t *ip;
160tcphdr_t *tcp;
161ipnat_t *nat;
162{
163	aproxy_t *apr = nat->in_apr;
164	u_short dport = nat->in_dport;
165
166	if (!apr || (apr->apr_flags & APR_DELETE) ||
167	    (ip->ip_p != apr->apr_p))
168		return 0;
169	if ((tcp && (tcp->th_dport != dport)) || (!tcp && dport))
170		return 0;
171	return 1;
172}
173
174
175/*
176 * Allocate a new application proxy structure and fill it in with the
177 * relevant details.  call the init function once complete, prior to
178 * returning.
179 */
180static ap_session_t *appr_new_session(apr, ip, fin, nat)
181aproxy_t *apr;
182ip_t *ip;
183fr_info_t *fin;
184nat_t *nat;
185{
186	register ap_session_t *aps;
187
188	if (!apr || (apr->apr_flags & APR_DELETE) || (ip->ip_p != apr->apr_p))
189		return NULL;
190
191	KMALLOC(aps, ap_session_t *);
192	if (!aps)
193		return NULL;
194	bzero((char *)aps, sizeof(*aps));
195	aps->aps_p = ip->ip_p;
196	aps->aps_data = NULL;
197	aps->aps_apr = apr;
198	aps->aps_psiz = 0;
199	if (apr->apr_new != NULL)
200		if ((*apr->apr_new)(fin, ip, aps, nat) == -1) {
201			KFREE(aps);
202			return NULL;
203		}
204	aps->aps_nat = nat;
205	aps->aps_next = ap_sess_list;
206	ap_sess_list = aps;
207	return aps;
208}
209
210
211/*
212 * check to see if a packet should be passed through an active proxy routine
213 * if one has been setup for it.
214 */
215int appr_check(ip, fin, nat)
216ip_t *ip;
217fr_info_t *fin;
218nat_t *nat;
219{
220	ap_session_t *aps;
221	aproxy_t *apr;
222	tcphdr_t *tcp = NULL;
223	u_32_t sum;
224	short rv;
225	int err;
226
227	if (nat->nat_aps == NULL)
228		nat->nat_aps = appr_new_session(nat->nat_ptr->in_apr, ip,
229						fin, nat);
230	aps = nat->nat_aps;
231	if ((aps != NULL) && (aps->aps_p == ip->ip_p)) {
232		if (ip->ip_p == IPPROTO_TCP) {
233			tcp = (tcphdr_t *)fin->fin_dp;
234			/*
235			 * verify that the checksum is correct.  If not, then
236			 * don't do anything with this packet.
237			 */
238#if SOLARIS && defined(_KERNEL)
239			sum = fr_tcpsum(fin->fin_qfm, ip, tcp);
240#else
241			sum = fr_tcpsum(*(mb_t **)fin->fin_mp, ip, tcp);
242#endif
243			if (sum != tcp->th_sum) {
244				frstats[fin->fin_out].fr_tcpbad++;
245				return -1;
246			}
247		}
248
249		apr = aps->aps_apr;
250		err = 0;
251		if (fin->fin_out != 0) {
252			if (apr->apr_outpkt != NULL)
253				err = (*apr->apr_outpkt)(fin, ip, aps, nat);
254		} else {
255			if (apr->apr_inpkt != NULL)
256				err = (*apr->apr_inpkt)(fin, ip, aps, nat);
257		}
258
259		rv = APR_EXIT(err);
260		if (rv == -1)
261			return rv;
262
263		if (tcp != NULL) {
264			err = appr_fixseqack(fin, ip, aps, APR_INC(err));
265#if SOLARIS && defined(_KERNEL)
266			tcp->th_sum = fr_tcpsum(fin->fin_qfm, ip, tcp);
267#else
268			tcp->th_sum = fr_tcpsum(*(mb_t **)fin->fin_mp, ip, tcp);
269#endif
270		}
271		aps->aps_bytes += ip->ip_len;
272		aps->aps_pkts++;
273		return 1;
274	}
275	return 0;
276}
277
278
279aproxy_t *appr_match(pr, name)
280u_int pr;
281char *name;
282{
283	aproxy_t *ap;
284
285	for (ap = ap_proxies; ap->apr_p; ap++)
286		if ((ap->apr_p == pr) &&
287		    !strncmp(name, ap->apr_label, sizeof(ap->apr_label))) {
288			ap->apr_ref++;
289			return ap;
290		}
291
292	for (ap = ap_proxylist; ap; ap = ap->apr_next)
293		if ((ap->apr_p == pr) &&
294		    !strncmp(name, ap->apr_label, sizeof(ap->apr_label))) {
295			ap->apr_ref++;
296			return ap;
297		}
298	return NULL;
299}
300
301
302void appr_free(ap)
303aproxy_t *ap;
304{
305	ap->apr_ref--;
306}
307
308
309void aps_free(aps)
310ap_session_t *aps;
311{
312	ap_session_t *a, **ap;
313
314	if (!aps)
315		return;
316
317	for (ap = &ap_sess_list; (a = *ap); ap = &a->aps_next)
318		if (a == aps) {
319			*ap = a->aps_next;
320			break;
321		}
322
323	if ((aps->aps_data != NULL) && (aps->aps_psiz != 0))
324		KFREES(aps->aps_data, aps->aps_psiz);
325	KFREE(aps);
326}
327
328
329static int appr_fixseqack(fin, ip, aps, inc)
330fr_info_t *fin;
331ip_t *ip;
332ap_session_t *aps;
333int inc;
334{
335	int sel, ch = 0, out, nlen;
336	u_32_t seq1, seq2;
337	tcphdr_t *tcp;
338
339	tcp = (tcphdr_t *)fin->fin_dp;
340	out = fin->fin_out;
341	nlen = ip->ip_len;
342	nlen -= (ip->ip_hl << 2) + (tcp->th_off << 2);
343
344	if (out != 0) {
345		seq1 = (u_32_t)ntohl(tcp->th_seq);
346		sel = aps->aps_sel[out];
347
348		/* switch to other set ? */
349		if ((aps->aps_seqmin[!sel] > aps->aps_seqmin[sel]) &&
350		    (seq1 > aps->aps_seqmin[!sel]))
351			sel = aps->aps_sel[out] = !sel;
352
353		if (aps->aps_seqoff[sel]) {
354			seq2 = aps->aps_seqmin[sel] - aps->aps_seqoff[sel];
355			if (seq1 > seq2) {
356				seq2 = aps->aps_seqoff[sel];
357				seq1 += seq2;
358				tcp->th_seq = htonl(seq1);
359				ch = 1;
360			}
361		}
362
363		if (inc && (seq1 > aps->aps_seqmin[!sel])) {
364			aps->aps_seqmin[!sel] = seq1 + nlen - 1;
365			aps->aps_seqoff[!sel] = aps->aps_seqoff[sel] + inc;
366		}
367
368		/***/
369
370		seq1 = ntohl(tcp->th_ack);
371		sel = aps->aps_sel[1 - out];
372
373		/* switch to other set ? */
374		if ((aps->aps_ackmin[!sel] > aps->aps_ackmin[sel]) &&
375		    (seq1 > aps->aps_ackmin[!sel]))
376			sel = aps->aps_sel[1 - out] = !sel;
377
378		if (aps->aps_ackoff[sel] && (seq1 > aps->aps_ackmin[sel])) {
379			seq2 = aps->aps_ackoff[sel];
380			tcp->th_ack = htonl(seq1 - seq2);
381			ch = 1;
382		}
383	} else {
384		seq1 = ntohl(tcp->th_seq);
385		sel = aps->aps_sel[out];
386
387		/* switch to other set ? */
388		if ((aps->aps_ackmin[!sel] > aps->aps_ackmin[sel]) &&
389		    (seq1 > aps->aps_ackmin[!sel]))
390			sel = aps->aps_sel[out] = !sel;
391
392		if (aps->aps_ackoff[sel]) {
393			seq2 = aps->aps_ackmin[sel] -
394			       aps->aps_ackoff[sel];
395			if (seq1 > seq2) {
396				seq2 = aps->aps_ackoff[sel];
397				seq1 += seq2;
398				tcp->th_seq = htonl(seq1);
399				ch = 1;
400			}
401		}
402
403		if (inc && (seq1 > aps->aps_ackmin[!sel])) {
404			aps->aps_ackmin[!sel] = seq1 + nlen - 1;
405			aps->aps_ackoff[!sel] = aps->aps_ackoff[sel] + inc;
406		}
407
408		/***/
409
410		seq1 = ntohl(tcp->th_ack);
411		sel = aps->aps_sel[1 - out];
412
413		/* switch to other set ? */
414		if ((aps->aps_seqmin[!sel] > aps->aps_seqmin[sel]) &&
415		    (seq1 > aps->aps_seqmin[!sel]))
416			sel = aps->aps_sel[1 - out] = !sel;
417
418		if (aps->aps_seqoff[sel] && (seq1 > aps->aps_seqmin[sel])) {
419			seq2 = aps->aps_seqoff[sel];
420			tcp->th_ack = htonl(seq1 - seq2);
421			ch = 1;
422		}
423	}
424	return ch ? 2 : 0;
425}
426
427
428int appr_init()
429{
430	aproxy_t *ap;
431	int err = 0;
432
433	for (ap = ap_proxies; ap->apr_p; ap++) {
434		err = (*ap->apr_init)();
435		if (err != 0)
436			break;
437	}
438	return err;
439}
440
441
442void appr_unload()
443{
444	aproxy_t *ap;
445
446	for (ap = ap_proxies; ap->apr_p; ap++)
447		if (ap->apr_fini)
448			(*ap->apr_fini)();
449	for (ap = ap_proxylist; ap; ap = ap->apr_next)
450		if (ap->apr_fini)
451			(*ap->apr_fini)();
452}
453