ip_proxy.c revision 145522
1145522Sdarrenr/*	$FreeBSD: head/sys/contrib/ipfilter/netinet/ip_proxy.c 145522 2005-04-25 18:43:14Z darrenr $	*/
2145522Sdarrenr
353642Sguido/*
4145522Sdarrenr * Copyright (C) 1997-2003 by Darren Reed.
553642Sguido *
680482Sdarrenr * See the IPFILTER.LICENCE file for details on licencing.
753642Sguido */
8145522Sdarrenr#if defined(KERNEL) || defined(_KERNEL)
9145522Sdarrenr# undef KERNEL
10145522Sdarrenr# undef _KERNEL
11145522Sdarrenr# define        KERNEL	1
12145522Sdarrenr# define        _KERNEL	1
1353642Sguido#endif
1453642Sguido#include <sys/errno.h>
1553642Sguido#include <sys/types.h>
1653642Sguido#include <sys/param.h>
1753642Sguido#include <sys/time.h>
1853642Sguido#include <sys/file.h>
1953642Sguido#include <sys/fcntl.h>
20145522Sdarrenr#if !defined(_KERNEL) && !defined(__KERNEL__)
2153642Sguido# include <stdio.h>
2253642Sguido# include <string.h>
2353642Sguido# include <stdlib.h>
24145522Sdarrenr# include <ctype.h>
25145522Sdarrenr# define _KERNEL
26145522Sdarrenr# ifdef __OpenBSD__
27145522Sdarrenrstruct file;
28145522Sdarrenr# endif
29145522Sdarrenr# include <sys/uio.h>
30145522Sdarrenr# undef _KERNEL
3153642Sguido#endif
32145522Sdarrenr#if !defined(linux)
3353642Sguido# include <sys/protosw.h>
3453642Sguido#endif
3553642Sguido#include <sys/socket.h>
3653642Sguido#if defined(_KERNEL)
37145522Sdarrenr# if !defined(__NetBSD__) && !defined(sun) && !defined(__osf__) && \
38145522Sdarrenr     !defined(__OpenBSD__) && !defined(__hpux) && !defined(__sgi)
39145522Sdarrenr#  include <sys/ctype.h>
4053642Sguido# endif
41145522Sdarrenr# include <sys/systm.h>
42145522Sdarrenr# if !defined(__SVR4) && !defined(__svr4__)
4353642Sguido#  include <sys/mbuf.h>
4453642Sguido# endif
45145522Sdarrenr#endif
46145522Sdarrenr#if defined(_KERNEL) && (__FreeBSD_version >= 220000)
47145522Sdarrenr# include <sys/filio.h>
48145522Sdarrenr# include <sys/fcntl.h>
49145522Sdarrenr# if (__FreeBSD_version >= 300000) && !defined(IPFILTER_LKM)
50145522Sdarrenr#  include "opt_ipfilter.h"
51145522Sdarrenr# endif
5253642Sguido#else
53145522Sdarrenr# include <sys/ioctl.h>
54145522Sdarrenr#endif
55145522Sdarrenr#if defined(__SVR4) || defined(__svr4__)
5653642Sguido# include <sys/byteorder.h>
5753642Sguido# ifdef _KERNEL
5853642Sguido#  include <sys/dditypes.h>
5953642Sguido# endif
6053642Sguido# include <sys/stream.h>
6153642Sguido# include <sys/kmem.h>
6253642Sguido#endif
6353642Sguido#if __FreeBSD__ > 2
6453642Sguido# include <sys/queue.h>
6553642Sguido#endif
6653642Sguido#include <net/if.h>
6753642Sguido#ifdef sun
6853642Sguido# include <net/af.h>
6953642Sguido#endif
7053642Sguido#include <net/route.h>
7153642Sguido#include <netinet/in.h>
7253642Sguido#include <netinet/in_systm.h>
7353642Sguido#include <netinet/ip.h>
7453642Sguido#ifndef linux
7553642Sguido# include <netinet/ip_var.h>
7653642Sguido#endif
7753642Sguido#include <netinet/tcp.h>
7853642Sguido#include <netinet/udp.h>
7953642Sguido#include <netinet/ip_icmp.h>
8053642Sguido#include "netinet/ip_compat.h"
8153642Sguido#include <netinet/tcpip.h>
8253642Sguido#include "netinet/ip_fil.h"
8353642Sguido#include "netinet/ip_nat.h"
8453642Sguido#include "netinet/ip_state.h"
8592685Sdarrenr#include "netinet/ip_proxy.h"
8653642Sguido#if (__FreeBSD_version >= 300000)
8753642Sguido# include <sys/malloc.h>
8853642Sguido#endif
8953642Sguido
90145522Sdarrenr#include "netinet/ip_ftp_pxy.c"
91145522Sdarrenr#include "netinet/ip_rcmd_pxy.c"
92145522Sdarrenr# include "netinet/ip_pptp_pxy.c"
93145522Sdarrenr#if defined(_KERNEL)
94145522Sdarrenr# include "netinet/ip_irc_pxy.c"
95145522Sdarrenr# include "netinet/ip_raudio_pxy.c"
96145522Sdarrenr# include "netinet/ip_h323_pxy.c"
97145522Sdarrenr# ifdef	IPFILTER_PRO
98145522Sdarrenr#  include "netinet/ip_msnrpc_pxy.c"
99145522Sdarrenr# endif
100145522Sdarrenr# include "netinet/ip_netbios_pxy.c"
10180482Sdarrenr#endif
102145522Sdarrenr#include "netinet/ip_ipsec_pxy.c"
103145522Sdarrenr#include "netinet/ip_rpcb_pxy.c"
10453642Sguido
105145522Sdarrenr/* END OF INCLUDES */
106145522Sdarrenr
107145522Sdarrenr#if !defined(lint)
108145522Sdarrenrstatic const char rcsid[] = "@(#)Id: ip_proxy.c,v 2.62.2.12 2005/03/03 14:28:24 darrenr Exp";
10992685Sdarrenr#endif
11080482Sdarrenr
11153642Sguidostatic int appr_fixseqack __P((fr_info_t *, ip_t *, ap_session_t *, int ));
11253642Sguido
11353642Sguido#define	AP_SESS_SIZE	53
11453642Sguido
11592685Sdarrenr#if defined(_KERNEL)
116145522Sdarrenrint		ipf_proxy_debug = 0;
117145522Sdarrenr#else
118145522Sdarrenrint		ipf_proxy_debug = 2;
11953642Sguido#endif
12053642Sguidoap_session_t	*ap_sess_tab[AP_SESS_SIZE];
12153642Sguidoap_session_t	*ap_sess_list = NULL;
12260855Sdarrenraproxy_t	*ap_proxylist = NULL;
12353642Sguidoaproxy_t	ap_proxies[] = {
12453642Sguido#ifdef	IPF_FTP_PROXY
125145522Sdarrenr	{ NULL, "ftp", (char)IPPROTO_TCP, 0, 0, ippr_ftp_init, ippr_ftp_fini,
12692685Sdarrenr	  ippr_ftp_new, NULL, ippr_ftp_in, ippr_ftp_out, NULL },
12753642Sguido#endif
128145522Sdarrenr#ifdef	IPF_IRC_PROXY
129145522Sdarrenr	{ NULL, "irc", (char)IPPROTO_TCP, 0, 0, ippr_irc_init, ippr_irc_fini,
130145522Sdarrenr	  ippr_irc_new, NULL, NULL, ippr_irc_out, NULL, NULL },
131145522Sdarrenr#endif
13253642Sguido#ifdef	IPF_RCMD_PROXY
133145522Sdarrenr	{ NULL, "rcmd", (char)IPPROTO_TCP, 0, 0, ippr_rcmd_init, ippr_rcmd_fini,
134145522Sdarrenr	  ippr_rcmd_new, NULL, ippr_rcmd_in, ippr_rcmd_out, NULL, NULL },
13553642Sguido#endif
13653642Sguido#ifdef	IPF_RAUDIO_PROXY
137145522Sdarrenr	{ NULL, "raudio", (char)IPPROTO_TCP, 0, 0, ippr_raudio_init, ippr_raudio_fini,
138145522Sdarrenr	  ippr_raudio_new, NULL, ippr_raudio_in, ippr_raudio_out, NULL, NULL },
13953642Sguido#endif
140145522Sdarrenr#ifdef	IPF_MSNRPC_PROXY
141145522Sdarrenr	{ NULL, "msnrpc", (char)IPPROTO_TCP, 0, 0, ippr_msnrpc_init, ippr_msnrpc_fini,
142145522Sdarrenr	  ippr_msnrpc_new, NULL, ippr_msnrpc_in, ippr_msnrpc_out, NULL, NULL },
14392685Sdarrenr#endif
14492685Sdarrenr#ifdef	IPF_NETBIOS_PROXY
145145522Sdarrenr	{ NULL, "netbios", (char)IPPROTO_UDP, 0, 0, ippr_netbios_init, ippr_netbios_fini,
146145522Sdarrenr	  NULL, NULL, NULL, ippr_netbios_out, NULL, NULL },
14792685Sdarrenr#endif
148145522Sdarrenr#ifdef	IPF_IPSEC_PROXY
149145522Sdarrenr	{ NULL, "ipsec", (char)IPPROTO_UDP, 0, 0,
150145522Sdarrenr	  ippr_ipsec_init, ippr_ipsec_fini, ippr_ipsec_new, ippr_ipsec_del,
151145522Sdarrenr	  ippr_ipsec_inout, ippr_ipsec_inout, ippr_ipsec_match, NULL },
152145522Sdarrenr#endif
153145522Sdarrenr#ifdef	IPF_PPTP_PROXY
154145522Sdarrenr	{ NULL, "pptp", (char)IPPROTO_TCP, 0, 0,
155145522Sdarrenr	  ippr_pptp_init, ippr_pptp_fini, ippr_pptp_new, ippr_pptp_del,
156145522Sdarrenr	  ippr_pptp_inout, ippr_pptp_inout, NULL, NULL },
157145522Sdarrenr#endif
15892685Sdarrenr#ifdef  IPF_H323_PROXY
159145522Sdarrenr	{ NULL, "h323", (char)IPPROTO_TCP, 0, 0, ippr_h323_init, ippr_h323_fini,
160145522Sdarrenr	  ippr_h323_new, ippr_h323_del, ippr_h323_in, NULL, NULL },
161145522Sdarrenr	{ NULL, "h245", (char)IPPROTO_TCP, 0, 0, NULL, NULL,
162145522Sdarrenr	  ippr_h245_new, NULL, NULL, ippr_h245_out, NULL },
163145522Sdarrenr#endif
164145522Sdarrenr#ifdef	IPF_RPCB_PROXY
165145522Sdarrenr# if 0
166145522Sdarrenr	{ NULL, "rpcbt", (char)IPPROTO_TCP, 0, 0,
167145522Sdarrenr	  ippr_rpcb_init, ippr_rpcb_fini, ippr_rpcb_new, ippr_rpcb_del,
168145522Sdarrenr	  ippr_rpcb_in, ippr_rpcb_out, NULL, NULL },
169145522Sdarrenr# endif
170145522Sdarrenr	{ NULL, "rpcbu", (char)IPPROTO_UDP, 0, 0,
171145522Sdarrenr	  ippr_rpcb_init, ippr_rpcb_fini, ippr_rpcb_new, ippr_rpcb_del,
172145522Sdarrenr	  ippr_rpcb_in, ippr_rpcb_out, NULL, NULL },
173145522Sdarrenr#endif
174145522Sdarrenr	{ NULL, "", '\0', 0, 0, NULL, NULL, NULL, NULL }
17553642Sguido};
17653642Sguido
17792685Sdarrenr/*
17892685Sdarrenr * Dynamically add a new kernel proxy.  Ensure that it is unique in the
17992685Sdarrenr * collection compiled in and dynamically added.
18092685Sdarrenr */
18160855Sdarrenrint appr_add(ap)
18260855Sdarrenraproxy_t *ap;
18360855Sdarrenr{
18460855Sdarrenr	aproxy_t *a;
18560855Sdarrenr
18660855Sdarrenr	for (a = ap_proxies; a->apr_p; a++)
18760855Sdarrenr		if ((a->apr_p == ap->apr_p) &&
18860855Sdarrenr		    !strncmp(a->apr_label, ap->apr_label,
189145522Sdarrenr			     sizeof(ap->apr_label))) {
190145522Sdarrenr			if (ipf_proxy_debug > 1)
191145522Sdarrenr				printf("appr_add: %s/%d already present (B)\n",
192145522Sdarrenr				       a->apr_label, a->apr_p);
19360855Sdarrenr			return -1;
194145522Sdarrenr		}
19560855Sdarrenr
196145522Sdarrenr	for (a = ap_proxylist; a->apr_p; a = a->apr_next)
19760855Sdarrenr		if ((a->apr_p == ap->apr_p) &&
19860855Sdarrenr		    !strncmp(a->apr_label, ap->apr_label,
199145522Sdarrenr			     sizeof(ap->apr_label))) {
200145522Sdarrenr			if (ipf_proxy_debug > 1)
201145522Sdarrenr				printf("appr_add: %s/%d already present (D)\n",
202145522Sdarrenr				       a->apr_label, a->apr_p);
20360855Sdarrenr			return -1;
204145522Sdarrenr		}
20560855Sdarrenr	ap->apr_next = ap_proxylist;
20660855Sdarrenr	ap_proxylist = ap;
207145522Sdarrenr	if (ap->apr_init != NULL)
208145522Sdarrenr		return (*ap->apr_init)();
209145522Sdarrenr	return 0;
21060855Sdarrenr}
21160855Sdarrenr
21260855Sdarrenr
21392685Sdarrenr/*
214145522Sdarrenr * Check to see if the proxy this control request has come through for
215145522Sdarrenr * exists, and if it does and it has a control function then invoke that
216145522Sdarrenr * control function.
217145522Sdarrenr */
218145522Sdarrenrint appr_ctl(ctl)
219145522Sdarrenrap_ctl_t *ctl;
220145522Sdarrenr{
221145522Sdarrenr	aproxy_t *a;
222145522Sdarrenr	int error;
223145522Sdarrenr
224145522Sdarrenr	a = appr_lookup(ctl->apc_p, ctl->apc_label);
225145522Sdarrenr	if (a == NULL) {
226145522Sdarrenr		if (ipf_proxy_debug > 1)
227145522Sdarrenr			printf("appr_ctl: can't find %s/%d\n",
228145522Sdarrenr				ctl->apc_label, ctl->apc_p);
229145522Sdarrenr		error = ESRCH;
230145522Sdarrenr	} else if (a->apr_ctl == NULL) {
231145522Sdarrenr		if (ipf_proxy_debug > 1)
232145522Sdarrenr			printf("appr_ctl: no ctl function for %s/%d\n",
233145522Sdarrenr				ctl->apc_label, ctl->apc_p);
234145522Sdarrenr		error = ENXIO;
235145522Sdarrenr	} else {
236145522Sdarrenr		error = (*a->apr_ctl)(a, ctl);
237145522Sdarrenr		if ((error != 0) && (ipf_proxy_debug > 1))
238145522Sdarrenr			printf("appr_ctl: %s/%d ctl error %d\n",
239145522Sdarrenr				a->apr_label, a->apr_p, error);
240145522Sdarrenr	}
241145522Sdarrenr	return error;
242145522Sdarrenr}
243145522Sdarrenr
244145522Sdarrenr
245145522Sdarrenr/*
24692685Sdarrenr * Delete a proxy that has been added dynamically from those available.
24792685Sdarrenr * If it is in use, return 1 (do not destroy NOW), not in use 0 or -1
24892685Sdarrenr * if it cannot be matched.
24992685Sdarrenr */
25060855Sdarrenrint appr_del(ap)
25160855Sdarrenraproxy_t *ap;
25260855Sdarrenr{
25360855Sdarrenr	aproxy_t *a, **app;
25460855Sdarrenr
255145522Sdarrenr	for (app = &ap_proxylist; ((a = *app) != NULL); app = &a->apr_next)
25660855Sdarrenr		if (a == ap) {
25792685Sdarrenr			a->apr_flags |= APR_DELETE;
25892685Sdarrenr			*app = a->apr_next;
259145522Sdarrenr			if (ap->apr_ref != 0) {
260145522Sdarrenr				if (ipf_proxy_debug > 2)
261145522Sdarrenr					printf("appr_del: orphaning %s/%d\n",
262145522Sdarrenr						ap->apr_label, ap->apr_p);
26360855Sdarrenr				return 1;
264145522Sdarrenr			}
26560855Sdarrenr			return 0;
26660855Sdarrenr		}
267145522Sdarrenr	if (ipf_proxy_debug > 1)
268145522Sdarrenr		printf("appr_del: proxy %lx not found\n", (u_long)ap);
26960855Sdarrenr	return -1;
27060855Sdarrenr}
27160855Sdarrenr
27260855Sdarrenr
27392685Sdarrenr/*
27492685Sdarrenr * Return 1 if the packet is a good match against a proxy, else 0.
27592685Sdarrenr */
276145522Sdarrenrint appr_ok(fin, tcp, nat)
277145522Sdarrenrfr_info_t *fin;
27853642Sguidotcphdr_t *tcp;
27953642Sguidoipnat_t *nat;
28053642Sguido{
28153642Sguido	aproxy_t *apr = nat->in_apr;
28253642Sguido	u_short dport = nat->in_dport;
28353642Sguido
28492685Sdarrenr	if ((apr == NULL) || (apr->apr_flags & APR_DELETE) ||
285145522Sdarrenr	    (fin->fin_p != apr->apr_p))
28653642Sguido		return 0;
287145522Sdarrenr	if ((tcp == NULL) && dport)
28853642Sguido		return 0;
28953642Sguido	return 1;
29053642Sguido}
29153642Sguido
29253642Sguido
293145522Sdarrenrint appr_ioctl(data, cmd, mode)
294145522Sdarrenrcaddr_t data;
295145522Sdarrenrioctlcmd_t cmd;
296145522Sdarrenrint mode;
297145522Sdarrenr{
298145522Sdarrenr	ap_ctl_t ctl;
299145522Sdarrenr	caddr_t ptr;
300145522Sdarrenr	int error;
301145522Sdarrenr
302145522Sdarrenr	mode = mode;	/* LINT */
303145522Sdarrenr
304145522Sdarrenr	switch (cmd)
305145522Sdarrenr	{
306145522Sdarrenr	case SIOCPROXY :
307145522Sdarrenr		BCOPYIN(data, &ctl, sizeof(ctl));
308145522Sdarrenr		ptr = NULL;
309145522Sdarrenr
310145522Sdarrenr		if (ctl.apc_dsize > 0) {
311145522Sdarrenr			KMALLOCS(ptr, caddr_t, ctl.apc_dsize);
312145522Sdarrenr			if (ptr == NULL)
313145522Sdarrenr				error = ENOMEM;
314145522Sdarrenr			else {
315145522Sdarrenr				error = copyinptr(ctl.apc_data, ptr,
316145522Sdarrenr						  ctl.apc_dsize);
317145522Sdarrenr				if (error == 0)
318145522Sdarrenr					ctl.apc_data = ptr;
319145522Sdarrenr			}
320145522Sdarrenr		} else {
321145522Sdarrenr			ctl.apc_data = NULL;
322145522Sdarrenr			error = 0;
323145522Sdarrenr		}
324145522Sdarrenr
325145522Sdarrenr		if (error == 0)
326145522Sdarrenr			error = appr_ctl(&ctl);
327145522Sdarrenr
328145522Sdarrenr		if ((ctl.apc_dsize > 0) && (ptr != NULL) &&
329145522Sdarrenr		    (ctl.apc_data == ptr)) {
330145522Sdarrenr			KFREES(ptr, ctl.apc_dsize);
331145522Sdarrenr		}
332145522Sdarrenr		break;
333145522Sdarrenr
334145522Sdarrenr	default :
335145522Sdarrenr		error = EINVAL;
336145522Sdarrenr	}
337145522Sdarrenr	return error;
338145522Sdarrenr}
339145522Sdarrenr
340145522Sdarrenr
34153642Sguido/*
34292685Sdarrenr * If a proxy has a match function, call that to do extended packet
34392685Sdarrenr * matching.
34492685Sdarrenr */
34592685Sdarrenrint appr_match(fin, nat)
34692685Sdarrenrfr_info_t *fin;
34792685Sdarrenrnat_t *nat;
34892685Sdarrenr{
34992685Sdarrenr	aproxy_t *apr;
35092685Sdarrenr	ipnat_t *ipn;
351145522Sdarrenr	int result;
35292685Sdarrenr
35392685Sdarrenr	ipn = nat->nat_ptr;
354145522Sdarrenr	if (ipf_proxy_debug > 8)
355145522Sdarrenr		printf("appr_match(%lx,%lx) aps %lx ptr %lx\n",
356145522Sdarrenr			(u_long)fin, (u_long)nat, (u_long)nat->nat_aps,
357145522Sdarrenr			(u_long)ipn);
358145522Sdarrenr
359145522Sdarrenr	if ((fin->fin_flx & (FI_SHORT|FI_BAD)) != 0) {
360145522Sdarrenr		if (ipf_proxy_debug > 0)
361145522Sdarrenr			printf("appr_match: flx 0x%x (BAD|SHORT)\n",
362145522Sdarrenr				fin->fin_flx);
36392685Sdarrenr		return -1;
364145522Sdarrenr	}
365145522Sdarrenr
36692685Sdarrenr	apr = ipn->in_apr;
367145522Sdarrenr	if ((apr == NULL) || (apr->apr_flags & APR_DELETE)) {
368145522Sdarrenr		if (ipf_proxy_debug > 0)
369145522Sdarrenr			printf("appr_match:apr %lx apr_flags 0x%x\n",
370145522Sdarrenr				(u_long)apr, apr ? apr->apr_flags : 0);
37192685Sdarrenr		return -1;
372145522Sdarrenr	}
373145522Sdarrenr
374145522Sdarrenr	if (apr->apr_match != NULL) {
375145522Sdarrenr		result = (*apr->apr_match)(fin, nat->nat_aps, nat);
376145522Sdarrenr		if (result != 0) {
377145522Sdarrenr			if (ipf_proxy_debug > 4)
378145522Sdarrenr				printf("appr_match: result %d\n", result);
37992685Sdarrenr			return -1;
380145522Sdarrenr		}
381145522Sdarrenr	}
38292685Sdarrenr	return 0;
38392685Sdarrenr}
38492685Sdarrenr
38592685Sdarrenr
38692685Sdarrenr/*
38753642Sguido * Allocate a new application proxy structure and fill it in with the
38853642Sguido * relevant details.  call the init function once complete, prior to
38953642Sguido * returning.
39053642Sguido */
391145522Sdarrenrint appr_new(fin, nat)
39292685Sdarrenrfr_info_t *fin;
39353642Sguidonat_t *nat;
39453642Sguido{
39553642Sguido	register ap_session_t *aps;
39692685Sdarrenr	aproxy_t *apr;
39753642Sguido
398145522Sdarrenr	if (ipf_proxy_debug > 8)
399145522Sdarrenr		printf("appr_new(%lx,%lx) \n", (u_long)fin, (u_long)nat);
400145522Sdarrenr
401145522Sdarrenr	if ((nat->nat_ptr == NULL) || (nat->nat_aps != NULL)) {
402145522Sdarrenr		if (ipf_proxy_debug > 0)
403145522Sdarrenr			printf("appr_new: nat_ptr %lx nat_aps %lx\n",
404145522Sdarrenr				(u_long)nat->nat_ptr, (u_long)nat->nat_aps);
40592685Sdarrenr		return -1;
406145522Sdarrenr	}
40792685Sdarrenr
40892685Sdarrenr	apr = nat->nat_ptr->in_apr;
40992685Sdarrenr
410145522Sdarrenr	if ((apr->apr_flags & APR_DELETE) ||
411145522Sdarrenr	    (fin->fin_p != apr->apr_p)) {
412145522Sdarrenr		if (ipf_proxy_debug > 2)
413145522Sdarrenr			printf("appr_new: apr_flags 0x%x p %d/%d\n",
414145522Sdarrenr				apr->apr_flags, fin->fin_p, apr->apr_p);
41592685Sdarrenr		return -1;
416145522Sdarrenr	}
41753642Sguido
41853642Sguido	KMALLOC(aps, ap_session_t *);
419145522Sdarrenr	if (!aps) {
420145522Sdarrenr		if (ipf_proxy_debug > 0)
421145522Sdarrenr			printf("appr_new: malloc failed (%lu)\n",
422145522Sdarrenr				(u_long)sizeof(ap_session_t));
42392685Sdarrenr		return -1;
424145522Sdarrenr	}
425145522Sdarrenr
42653642Sguido	bzero((char *)aps, sizeof(*aps));
427145522Sdarrenr	aps->aps_p = fin->fin_p;
42853642Sguido	aps->aps_data = NULL;
42953642Sguido	aps->aps_apr = apr;
43053642Sguido	aps->aps_psiz = 0;
43160855Sdarrenr	if (apr->apr_new != NULL)
432145522Sdarrenr		if ((*apr->apr_new)(fin, aps, nat) == -1) {
43392685Sdarrenr			if ((aps->aps_data != NULL) && (aps->aps_psiz != 0)) {
43492685Sdarrenr				KFREES(aps->aps_data, aps->aps_psiz);
43592685Sdarrenr			}
43660855Sdarrenr			KFREE(aps);
437145522Sdarrenr			if (ipf_proxy_debug > 2)
438145522Sdarrenr				printf("appr_new: new(%lx) failed\n",
439145522Sdarrenr					(u_long)apr->apr_new);
44092685Sdarrenr			return -1;
44160855Sdarrenr		}
44260855Sdarrenr	aps->aps_nat = nat;
44360855Sdarrenr	aps->aps_next = ap_sess_list;
44453642Sguido	ap_sess_list = aps;
44592685Sdarrenr	nat->nat_aps = aps;
44692685Sdarrenr
44792685Sdarrenr	return 0;
44853642Sguido}
44953642Sguido
45053642Sguido
45153642Sguido/*
452145522Sdarrenr * Check to see if a packet should be passed through an active proxy routine
453145522Sdarrenr * if one has been setup for it.  We don't need to check the checksum here if
454145522Sdarrenr * IPFILTER_CKSUM is defined because if it is, a failed check causes FI_BAD
455145522Sdarrenr * to be set.
45653642Sguido */
457145522Sdarrenrint appr_check(fin, nat)
45853642Sguidofr_info_t *fin;
45953642Sguidonat_t *nat;
46053642Sguido{
46180482Sdarrenr#if SOLARIS && defined(_KERNEL) && (SOLARIS2 >= 6)
462145522Sdarrenr# if defined(ICK_VALID)
463145522Sdarrenr	mb_t *m;
464145522Sdarrenr# endif
46580482Sdarrenr	int dosum = 1;
46680482Sdarrenr#endif
46780482Sdarrenr	tcphdr_t *tcp = NULL;
468145522Sdarrenr	udphdr_t *udp = NULL;
46953642Sguido	ap_session_t *aps;
47053642Sguido	aproxy_t *apr;
471145522Sdarrenr	ip_t *ip;
47260855Sdarrenr	short rv;
47353642Sguido	int err;
474145522Sdarrenr#if !defined(_KERNEL) || defined(MENTAT) || defined(__sgi)
475145522Sdarrenr	u_32_t s1, s2, sd;
476145522Sdarrenr#endif
47753642Sguido
478145522Sdarrenr	if (fin->fin_flx & FI_BAD) {
479145522Sdarrenr		if (ipf_proxy_debug > 0)
480145522Sdarrenr			printf("appr_check: flx 0x%x (BAD)\n", fin->fin_flx);
481145522Sdarrenr		return -1;
482145522Sdarrenr	}
483145522Sdarrenr
484145522Sdarrenr#ifndef IPFILTER_CKSUM
485145522Sdarrenr	if ((fin->fin_out == 0) && (fr_checkl4sum(fin) == -1)) {
486145522Sdarrenr		if (ipf_proxy_debug > 0)
487145522Sdarrenr			printf("appr_check: l4 checksum failure %d\n",
488145522Sdarrenr				fin->fin_p);
489145522Sdarrenr		if (fin->fin_p == IPPROTO_TCP)
490145522Sdarrenr			frstats[fin->fin_out].fr_tcpbad++;
491145522Sdarrenr		return -1;
492145522Sdarrenr	}
493145522Sdarrenr#endif
494145522Sdarrenr
49553642Sguido	aps = nat->nat_aps;
496145522Sdarrenr	if ((aps != NULL) && (aps->aps_p == fin->fin_p)) {
497145522Sdarrenr		/*
498145522Sdarrenr		 * If there is data in this packet to be proxied then try and
499145522Sdarrenr		 * get it all into the one buffer, else drop it.
500145522Sdarrenr		 */
501145522Sdarrenr#if defined(MENTAT) || defined(HAVE_M_PULLDOWN)
502145522Sdarrenr		if ((fin->fin_dlen > 0) && !(fin->fin_flx & FI_COALESCE))
503145522Sdarrenr			if (fr_coalesce(fin) == -1) {
504145522Sdarrenr				if (ipf_proxy_debug > 0)
505145522Sdarrenr					printf("appr_check: fr_coalesce failed %x\n", fin->fin_flx);
506145522Sdarrenr				return -1;
507145522Sdarrenr			}
508145522Sdarrenr#endif
509145522Sdarrenr		ip = fin->fin_ip;
510145522Sdarrenr
511145522Sdarrenr		switch (fin->fin_p)
512145522Sdarrenr		{
513145522Sdarrenr		case IPPROTO_TCP :
51453642Sguido			tcp = (tcphdr_t *)fin->fin_dp;
515145522Sdarrenr
516145522Sdarrenr#if SOLARIS && defined(_KERNEL) && (SOLARIS2 >= 6) && defined(ICK_VALID)
517145522Sdarrenr			m = fin->fin_qfm;
518145522Sdarrenr			if (dohwcksum && (m->b_ick_flag == ICK_VALID))
51980482Sdarrenr				dosum = 0;
52053642Sguido#endif
521102520Sdarrenr			/*
522110916Sdarrenr			 * Don't bother the proxy with these...or in fact,
523110916Sdarrenr			 * should we free up proxy stuff when seen?
524102520Sdarrenr			 */
525145522Sdarrenr			if ((fin->fin_tcpf & TH_RST) != 0)
526145522Sdarrenr				break;
527145522Sdarrenr			/*FALLTHROUGH*/
528145522Sdarrenr		case IPPROTO_UDP :
529145522Sdarrenr			udp = (udphdr_t *)fin->fin_dp;
530145522Sdarrenr			break;
531145522Sdarrenr		default :
532145522Sdarrenr			break;
53353642Sguido		}
53453642Sguido
53553642Sguido		apr = aps->aps_apr;
53653642Sguido		err = 0;
53753642Sguido		if (fin->fin_out != 0) {
53853642Sguido			if (apr->apr_outpkt != NULL)
539145522Sdarrenr				err = (*apr->apr_outpkt)(fin, aps, nat);
54053642Sguido		} else {
54153642Sguido			if (apr->apr_inpkt != NULL)
542145522Sdarrenr				err = (*apr->apr_inpkt)(fin, aps, nat);
54353642Sguido		}
54453642Sguido
54560855Sdarrenr		rv = APR_EXIT(err);
546145522Sdarrenr		if (((ipf_proxy_debug > 0) && (rv != 0)) ||
547145522Sdarrenr		    (ipf_proxy_debug > 8))
548145522Sdarrenr			printf("appr_check: out %d err %x rv %d\n",
549145522Sdarrenr				fin->fin_out, err, rv);
550145522Sdarrenr		if (rv == 1)
55192685Sdarrenr			return -1;
552145522Sdarrenr
55392685Sdarrenr		if (rv == 2) {
55492685Sdarrenr			appr_free(apr);
55592685Sdarrenr			nat->nat_aps = NULL;
55692685Sdarrenr			return -1;
55792685Sdarrenr		}
55860855Sdarrenr
559145522Sdarrenr		/*
560145522Sdarrenr		 * If err != 0 then the data size of the packet has changed
561145522Sdarrenr		 * so we need to recalculate the header checksums for the
562145522Sdarrenr		 * packet.
563145522Sdarrenr		 */
564145522Sdarrenr#if !defined(_KERNEL) || defined(MENTAT) || defined(__sgi)
565145522Sdarrenr		if (err != 0) {
566145522Sdarrenr			short adjlen = err & 0xffff;
567145522Sdarrenr
568145522Sdarrenr			s1 = LONG_SUM(ip->ip_len - adjlen);
569145522Sdarrenr			s2 = LONG_SUM(ip->ip_len);
570145522Sdarrenr			CALC_SUMD(s1, s2, sd);
571145522Sdarrenr			fix_outcksum(fin, &ip->ip_sum, sd);
572145522Sdarrenr		}
573145522Sdarrenr#endif
574145522Sdarrenr
575145522Sdarrenr		/*
576145522Sdarrenr		 * For TCP packets, we may need to adjust the sequence and
577145522Sdarrenr		 * acknowledgement numbers to reflect changes in size of the
578145522Sdarrenr		 * data stream.
579145522Sdarrenr		 *
580145522Sdarrenr		 * For both TCP and UDP, recalculate the layer 4 checksum,
581145522Sdarrenr		 * regardless, as we can't tell (here) if data has been
582145522Sdarrenr		 * changed or not.
583145522Sdarrenr		 */
58453642Sguido		if (tcp != NULL) {
58560855Sdarrenr			err = appr_fixseqack(fin, ip, aps, APR_INC(err));
58680482Sdarrenr#if SOLARIS && defined(_KERNEL) && (SOLARIS2 >= 6)
58780482Sdarrenr			if (dosum)
588145522Sdarrenr				tcp->th_sum = fr_cksum(fin->fin_qfm, ip,
589145522Sdarrenr						       IPPROTO_TCP, tcp);
59053642Sguido#else
591145522Sdarrenr			tcp->th_sum = fr_cksum(fin->fin_m, ip,
592145522Sdarrenr					       IPPROTO_TCP, tcp);
59353642Sguido#endif
594145522Sdarrenr		} else if ((udp != NULL) && (udp->uh_sum != 0)) {
595145522Sdarrenr#if SOLARIS && defined(_KERNEL) && (SOLARIS2 >= 6)
596145522Sdarrenr			if (dosum)
597145522Sdarrenr				udp->uh_sum = fr_cksum(fin->fin_qfm, ip,
598145522Sdarrenr						       IPPROTO_UDP, udp);
599145522Sdarrenr#else
600145522Sdarrenr			udp->uh_sum = fr_cksum(fin->fin_m, ip,
601145522Sdarrenr					       IPPROTO_UDP, udp);
602145522Sdarrenr#endif
60353642Sguido		}
604145522Sdarrenr		aps->aps_bytes += fin->fin_plen;
60553642Sguido		aps->aps_pkts++;
60660855Sdarrenr		return 1;
60753642Sguido	}
60860855Sdarrenr	return 0;
60953642Sguido}
61053642Sguido
61153642Sguido
61292685Sdarrenr/*
61392685Sdarrenr * Search for an proxy by the protocol it is being used with and its name.
61492685Sdarrenr */
61592685Sdarrenraproxy_t *appr_lookup(pr, name)
61653642Sguidou_int pr;
61753642Sguidochar *name;
61853642Sguido{
61953642Sguido	aproxy_t *ap;
62053642Sguido
621145522Sdarrenr	if (ipf_proxy_debug > 8)
622145522Sdarrenr		printf("appr_lookup(%d,%s)\n", pr, name);
623145522Sdarrenr
62453642Sguido	for (ap = ap_proxies; ap->apr_p; ap++)
62553642Sguido		if ((ap->apr_p == pr) &&
62653642Sguido		    !strncmp(name, ap->apr_label, sizeof(ap->apr_label))) {
62753642Sguido			ap->apr_ref++;
62853642Sguido			return ap;
62953642Sguido		}
63060855Sdarrenr
63160855Sdarrenr	for (ap = ap_proxylist; ap; ap = ap->apr_next)
63260855Sdarrenr		if ((ap->apr_p == pr) &&
63360855Sdarrenr		    !strncmp(name, ap->apr_label, sizeof(ap->apr_label))) {
63460855Sdarrenr			ap->apr_ref++;
63560855Sdarrenr			return ap;
63660855Sdarrenr		}
637145522Sdarrenr	if (ipf_proxy_debug > 2)
638145522Sdarrenr		printf("appr_lookup: failed for %d/%s\n", pr, name);
63953642Sguido	return NULL;
64053642Sguido}
64153642Sguido
64253642Sguido
64353642Sguidovoid appr_free(ap)
64453642Sguidoaproxy_t *ap;
64553642Sguido{
64653642Sguido	ap->apr_ref--;
64753642Sguido}
64853642Sguido
64953642Sguido
65053642Sguidovoid aps_free(aps)
65153642Sguidoap_session_t *aps;
65253642Sguido{
65353642Sguido	ap_session_t *a, **ap;
65492685Sdarrenr	aproxy_t *apr;
65553642Sguido
65653642Sguido	if (!aps)
65753642Sguido		return;
65853642Sguido
659145522Sdarrenr	for (ap = &ap_sess_list; ((a = *ap) != NULL); ap = &a->aps_next)
66053642Sguido		if (a == aps) {
66153642Sguido			*ap = a->aps_next;
66253642Sguido			break;
66353642Sguido		}
66453642Sguido
66592685Sdarrenr	apr = aps->aps_apr;
66692685Sdarrenr	if ((apr != NULL) && (apr->apr_del != NULL))
66792685Sdarrenr		(*apr->apr_del)(aps);
668110916Sdarrenr
66960855Sdarrenr	if ((aps->aps_data != NULL) && (aps->aps_psiz != 0))
67060855Sdarrenr		KFREES(aps->aps_data, aps->aps_psiz);
67160855Sdarrenr	KFREE(aps);
67253642Sguido}
67353642Sguido
67453642Sguido
675102520Sdarrenr/*
676102520Sdarrenr * returns 2 if ack or seq number in TCP header is changed, returns 0 otherwise
677102520Sdarrenr */
67853642Sguidostatic int appr_fixseqack(fin, ip, aps, inc)
67953642Sguidofr_info_t *fin;
68053642Sguidoip_t *ip;
68153642Sguidoap_session_t *aps;
68253642Sguidoint inc;
68353642Sguido{
68453642Sguido	int sel, ch = 0, out, nlen;
68553642Sguido	u_32_t seq1, seq2;
68653642Sguido	tcphdr_t *tcp;
68798004Sdarrenr	short inc2;
68853642Sguido
68953642Sguido	tcp = (tcphdr_t *)fin->fin_dp;
69053642Sguido	out = fin->fin_out;
691102520Sdarrenr	/*
692102520Sdarrenr	 * ip_len has already been adjusted by 'inc'.
693102520Sdarrenr	 */
69453642Sguido	nlen = ip->ip_len;
695145522Sdarrenr	nlen -= (IP_HL(ip) << 2) + (TCP_OFF(tcp) << 2);
696102520Sdarrenr
69798004Sdarrenr	inc2 = inc;
69898004Sdarrenr	inc = (int)inc2;
69953642Sguido
70053642Sguido	if (out != 0) {
70153642Sguido		seq1 = (u_32_t)ntohl(tcp->th_seq);
70253642Sguido		sel = aps->aps_sel[out];
70353642Sguido
70453642Sguido		/* switch to other set ? */
70553642Sguido		if ((aps->aps_seqmin[!sel] > aps->aps_seqmin[sel]) &&
706102520Sdarrenr		    (seq1 > aps->aps_seqmin[!sel])) {
707145522Sdarrenr			if (ipf_proxy_debug > 7)
708145522Sdarrenr				printf("proxy out switch set seq %d -> %d %x > %x\n",
709145522Sdarrenr					sel, !sel, seq1,
710145522Sdarrenr					aps->aps_seqmin[!sel]);
71153642Sguido			sel = aps->aps_sel[out] = !sel;
712110916Sdarrenr		}
71353642Sguido
71453642Sguido		if (aps->aps_seqoff[sel]) {
71553642Sguido			seq2 = aps->aps_seqmin[sel] - aps->aps_seqoff[sel];
71653642Sguido			if (seq1 > seq2) {
71753642Sguido				seq2 = aps->aps_seqoff[sel];
71853642Sguido				seq1 += seq2;
71953642Sguido				tcp->th_seq = htonl(seq1);
72053642Sguido				ch = 1;
72153642Sguido			}
72253642Sguido		}
72353642Sguido
72453642Sguido		if (inc && (seq1 > aps->aps_seqmin[!sel])) {
725102520Sdarrenr			aps->aps_seqmin[sel] = seq1 + nlen - 1;
726102520Sdarrenr			aps->aps_seqoff[sel] = aps->aps_seqoff[sel] + inc;
727145522Sdarrenr			if (ipf_proxy_debug > 7)
728145522Sdarrenr				printf("proxy seq set %d at %x to %d + %d\n",
729145522Sdarrenr					sel, aps->aps_seqmin[sel],
730145522Sdarrenr					aps->aps_seqoff[sel], inc);
73153642Sguido		}
73253642Sguido
73353642Sguido		/***/
73453642Sguido
73553642Sguido		seq1 = ntohl(tcp->th_ack);
73653642Sguido		sel = aps->aps_sel[1 - out];
73753642Sguido
73853642Sguido		/* switch to other set ? */
73953642Sguido		if ((aps->aps_ackmin[!sel] > aps->aps_ackmin[sel]) &&
740102520Sdarrenr		    (seq1 > aps->aps_ackmin[!sel])) {
741145522Sdarrenr			if (ipf_proxy_debug > 7)
742145522Sdarrenr				printf("proxy out switch set ack %d -> %d %x > %x\n",
743145522Sdarrenr					sel, !sel, seq1,
744145522Sdarrenr					aps->aps_ackmin[!sel]);
74553642Sguido			sel = aps->aps_sel[1 - out] = !sel;
746110916Sdarrenr		}
74753642Sguido
74853642Sguido		if (aps->aps_ackoff[sel] && (seq1 > aps->aps_ackmin[sel])) {
74953642Sguido			seq2 = aps->aps_ackoff[sel];
75053642Sguido			tcp->th_ack = htonl(seq1 - seq2);
75153642Sguido			ch = 1;
75253642Sguido		}
75353642Sguido	} else {
75453642Sguido		seq1 = ntohl(tcp->th_seq);
75553642Sguido		sel = aps->aps_sel[out];
75653642Sguido
75753642Sguido		/* switch to other set ? */
75853642Sguido		if ((aps->aps_ackmin[!sel] > aps->aps_ackmin[sel]) &&
759102520Sdarrenr		    (seq1 > aps->aps_ackmin[!sel])) {
760145522Sdarrenr			if (ipf_proxy_debug > 7)
761145522Sdarrenr				printf("proxy in switch set ack %d -> %d %x > %x\n",
762145522Sdarrenr					sel, !sel, seq1, aps->aps_ackmin[!sel]);
76353642Sguido			sel = aps->aps_sel[out] = !sel;
764110916Sdarrenr		}
76553642Sguido
76653642Sguido		if (aps->aps_ackoff[sel]) {
767102520Sdarrenr			seq2 = aps->aps_ackmin[sel] - aps->aps_ackoff[sel];
76853642Sguido			if (seq1 > seq2) {
76953642Sguido				seq2 = aps->aps_ackoff[sel];
77053642Sguido				seq1 += seq2;
77153642Sguido				tcp->th_seq = htonl(seq1);
77253642Sguido				ch = 1;
77353642Sguido			}
77453642Sguido		}
77553642Sguido
77653642Sguido		if (inc && (seq1 > aps->aps_ackmin[!sel])) {
77753642Sguido			aps->aps_ackmin[!sel] = seq1 + nlen - 1;
77853642Sguido			aps->aps_ackoff[!sel] = aps->aps_ackoff[sel] + inc;
779145522Sdarrenr
780145522Sdarrenr			if (ipf_proxy_debug > 7)
781145522Sdarrenr				printf("proxy ack set %d at %x to %d + %d\n",
782145522Sdarrenr					!sel, aps->aps_seqmin[!sel],
783145522Sdarrenr					aps->aps_seqoff[sel], inc);
78453642Sguido		}
78553642Sguido
78653642Sguido		/***/
78753642Sguido
78853642Sguido		seq1 = ntohl(tcp->th_ack);
78953642Sguido		sel = aps->aps_sel[1 - out];
79053642Sguido
79153642Sguido		/* switch to other set ? */
79253642Sguido		if ((aps->aps_seqmin[!sel] > aps->aps_seqmin[sel]) &&
793102520Sdarrenr		    (seq1 > aps->aps_seqmin[!sel])) {
794145522Sdarrenr			if (ipf_proxy_debug > 7)
795145522Sdarrenr				printf("proxy in switch set seq %d -> %d %x > %x\n",
796145522Sdarrenr					sel, !sel, seq1, aps->aps_seqmin[!sel]);
79753642Sguido			sel = aps->aps_sel[1 - out] = !sel;
798110916Sdarrenr		}
79953642Sguido
800102520Sdarrenr		if (aps->aps_seqoff[sel] != 0) {
801145522Sdarrenr			if (ipf_proxy_debug > 7)
802145522Sdarrenr				printf("sel %d seqoff %d seq1 %x seqmin %x\n",
803145522Sdarrenr					sel, aps->aps_seqoff[sel], seq1,
804145522Sdarrenr					aps->aps_seqmin[sel]);
805102520Sdarrenr			if (seq1 > aps->aps_seqmin[sel]) {
806102520Sdarrenr				seq2 = aps->aps_seqoff[sel];
807102520Sdarrenr				tcp->th_ack = htonl(seq1 - seq2);
808102520Sdarrenr				ch = 1;
809102520Sdarrenr			}
81053642Sguido		}
81153642Sguido	}
812145522Sdarrenr
813145522Sdarrenr	if (ipf_proxy_debug > 8)
814145522Sdarrenr		printf("appr_fixseqack: seq %x ack %x\n",
815145522Sdarrenr			ntohl(tcp->th_seq), ntohl(tcp->th_ack));
81653642Sguido	return ch ? 2 : 0;
81753642Sguido}
81853642Sguido
81953642Sguido
82092685Sdarrenr/*
82192685Sdarrenr * Initialise hook for kernel application proxies.
82292685Sdarrenr * Call the initialise routine for all the compiled in kernel proxies.
82392685Sdarrenr */
82453642Sguidoint appr_init()
82553642Sguido{
82653642Sguido	aproxy_t *ap;
82753642Sguido	int err = 0;
82853642Sguido
82953642Sguido	for (ap = ap_proxies; ap->apr_p; ap++) {
830145522Sdarrenr		if (ap->apr_init != NULL) {
831145522Sdarrenr			err = (*ap->apr_init)();
832145522Sdarrenr			if (err != 0)
833145522Sdarrenr				break;
834145522Sdarrenr		}
83553642Sguido	}
83653642Sguido	return err;
83753642Sguido}
83860855Sdarrenr
83960855Sdarrenr
84092685Sdarrenr/*
84192685Sdarrenr * Unload hook for kernel application proxies.
84292685Sdarrenr * Call the finialise routine for all the compiled in kernel proxies.
84392685Sdarrenr */
84460855Sdarrenrvoid appr_unload()
84560855Sdarrenr{
84660855Sdarrenr	aproxy_t *ap;
84760855Sdarrenr
84860855Sdarrenr	for (ap = ap_proxies; ap->apr_p; ap++)
849145522Sdarrenr		if (ap->apr_fini != NULL)
85060855Sdarrenr			(*ap->apr_fini)();
85160855Sdarrenr	for (ap = ap_proxylist; ap; ap = ap->apr_next)
852145522Sdarrenr		if (ap->apr_fini != NULL)
85360855Sdarrenr			(*ap->apr_fini)();
85460855Sdarrenr}
855