ip_proxy.c revision 60855
186091Sjhb/*
286091Sjhb * Copyright (C) 1997-2000 by Darren Reed.
386091Sjhb *
486091Sjhb * Redistribution and use in source and binary forms are permitted
586091Sjhb * provided that this notice is preserved and due credit is given
686091Sjhb * to the original author and the contributors.
786091Sjhb */
886091Sjhb#if !defined(lint)
986091Sjhb/*static const char rcsid[] = "@(#)$Id: ip_proxy.c,v 2.2.2.1 1999/09/19 12:18:19 darrenr Exp $";*/
1086091Sjhbstatic const char rcsid[] = "@(#)$FreeBSD: head/sys/contrib/ipfilter/netinet/ip_proxy.c 60855 2000-05-24 04:21:35Z darrenr $";
1186091Sjhb#endif
1286091Sjhb
1386091Sjhb#if defined(__FreeBSD__) && defined(KERNEL) && !defined(_KERNEL)
1486091Sjhb# define	_KERNEL
1586091Sjhb#endif
1686091Sjhb
1786091Sjhb#include <sys/errno.h>
1886091Sjhb#include <sys/types.h>
1986091Sjhb#include <sys/param.h>
2086091Sjhb#include <sys/time.h>
2186091Sjhb#include <sys/file.h>
2286091Sjhb#if !defined(__FreeBSD_version)
2386091Sjhb# include <sys/ioctl.h>
2486091Sjhb#endif
2586091Sjhb#include <sys/fcntl.h>
2686091Sjhb#include <sys/uio.h>
2786091Sjhb#if !defined(_KERNEL) && !defined(KERNEL)
28119482Sobrien# include <stdio.h>
29119482Sobrien# include <string.h>
30119482Sobrien# include <stdlib.h>
3186091Sjhb#endif
3286091Sjhb#ifndef	linux
3386091Sjhb# include <sys/protosw.h>
3486091Sjhb#endif
3586091Sjhb#include <sys/socket.h>
3686091Sjhb#if defined(_KERNEL)
3786091Sjhb# if !defined(linux)
3886091Sjhb#  include <sys/systm.h>
3986091Sjhb# else
4086091Sjhb#  include <linux/string.h>
4186091Sjhb# endif
4286091Sjhb#endif
4396654Sjhay#if !defined(__SVR4) && !defined(__svr4__)
44113083Sphk# ifndef linux
4586091Sjhb#  include <sys/mbuf.h>
4686091Sjhb# endif
4786091Sjhb#else
4886091Sjhb# include <sys/byteorder.h>
4986091Sjhb# ifdef _KERNEL
50226746Sjhb#  include <sys/dditypes.h>
5186091Sjhb# endif
5286091Sjhb# include <sys/stream.h>
5386091Sjhb# include <sys/kmem.h>
5486091Sjhb#endif
5586091Sjhb#if __FreeBSD__ > 2
5686091Sjhb# include <sys/queue.h>
5786091Sjhb#endif
5886091Sjhb#include <net/if.h>
5986091Sjhb#ifdef sun
6086091Sjhb# include <net/af.h>
6186091Sjhb#endif
6287599Sobrien#include <net/route.h>
6386091Sjhb#include <netinet/in.h>
6486091Sjhb#include <netinet/in_systm.h>
6586091Sjhb#include <netinet/ip.h>
6686091Sjhb#ifndef linux
6786091Sjhb# include <netinet/ip_var.h>
6886091Sjhb#endif
6986091Sjhb#include <netinet/tcp.h>
7086091Sjhb#include <netinet/udp.h>
7186091Sjhb#include <netinet/ip_icmp.h>
7286091Sjhb#include "netinet/ip_compat.h"
7386091Sjhb#include <netinet/tcpip.h>
7486091Sjhb#include "netinet/ip_fil.h"
7586091Sjhb#include "netinet/ip_proxy.h"
7686091Sjhb#include "netinet/ip_nat.h"
7786091Sjhb#include "netinet/ip_state.h"
7886091Sjhb#if (__FreeBSD_version >= 300000)
7986091Sjhb# include <sys/malloc.h>
8086091Sjhb#endif
8186091Sjhb
8286091Sjhb
8386091Sjhb#ifndef MIN
8486091Sjhb#define MIN(a,b)        (((a)<(b))?(a):(b))
8586091Sjhb#endif
8686091Sjhb
8786091Sjhbstatic ap_session_t *appr_new_session __P((aproxy_t *, ip_t *,
8886091Sjhb					   fr_info_t *, nat_t *));
8986091Sjhbstatic int appr_fixseqack __P((fr_info_t *, ip_t *, ap_session_t *, int ));
9086091Sjhb
9186091Sjhb
9286091Sjhb#define	AP_SESS_SIZE	53
9386091Sjhb
9486091Sjhb#if defined(_KERNEL) && !defined(linux)
9586091Sjhb#include "netinet/ip_ftp_pxy.c"
9686091Sjhb#include "netinet/ip_rcmd_pxy.c"
9786091Sjhb#include "netinet/ip_raudio_pxy.c"
9886091Sjhb#endif
9986091Sjhb
10086091Sjhbap_session_t	*ap_sess_tab[AP_SESS_SIZE];
10186091Sjhbap_session_t	*ap_sess_list = NULL;
10286091Sjhbaproxy_t	*ap_proxylist = NULL;
10386091Sjhbaproxy_t	ap_proxies[] = {
10486091Sjhb#ifdef	IPF_FTP_PROXY
10586091Sjhb	{ NULL, "ftp", (char)IPPROTO_TCP, 0, 0, ippr_ftp_init, NULL,
10686091Sjhb	  ippr_ftp_new, ippr_ftp_in, ippr_ftp_out },
10786091Sjhb#endif
10886091Sjhb#ifdef	IPF_RCMD_PROXY
10986091Sjhb	{ NULL, "rcmd", (char)IPPROTO_TCP, 0, 0, ippr_rcmd_init, NULL,
11086091Sjhb	  ippr_rcmd_new, NULL, ippr_rcmd_out },
11186091Sjhb#endif
11286091Sjhb#ifdef	IPF_RAUDIO_PROXY
11386091Sjhb	{ NULL, "raudio", (char)IPPROTO_TCP, 0, 0, ippr_raudio_init, NULL,
11486091Sjhb	  ippr_raudio_new, ippr_raudio_in, ippr_raudio_out },
11586091Sjhb#endif
11686091Sjhb	{ NULL, "", '\0', 0, 0, NULL, NULL }
11786091Sjhb};
11886091Sjhb
11986091Sjhb
12086091Sjhbint appr_add(ap)
12186091Sjhbaproxy_t *ap;
12286091Sjhb{
12386091Sjhb	aproxy_t *a;
12486091Sjhb
12586091Sjhb	for (a = ap_proxies; a->apr_p; a++)
12686091Sjhb		if ((a->apr_p == ap->apr_p) &&
12786091Sjhb		    !strncmp(a->apr_label, ap->apr_label,
12886091Sjhb			     sizeof(ap->apr_label)))
12986091Sjhb			return -1;
13086091Sjhb
13186091Sjhb	for (a = ap_proxylist; a->apr_p; a = a->apr_next)
13286091Sjhb		if ((a->apr_p == ap->apr_p) &&
13386091Sjhb		    !strncmp(a->apr_label, ap->apr_label,
13486091Sjhb			     sizeof(ap->apr_label)))
13586091Sjhb			return -1;
13686091Sjhb	ap->apr_next = ap_proxylist;
13786091Sjhb	ap_proxylist = ap;
13886091Sjhb	return (*ap->apr_init)();
13986091Sjhb}
14086091Sjhb
14186091Sjhb
14286091Sjhbint appr_del(ap)
14386091Sjhbaproxy_t *ap;
14486091Sjhb{
14586091Sjhb	aproxy_t *a, **app;
14686091Sjhb
14786091Sjhb	for (app = &ap_proxylist; (a = *app); app = &a->apr_next)
14886091Sjhb		if (a == ap) {
14986091Sjhb			if (ap->apr_ref != 0)
15086091Sjhb				return 1;
15186091Sjhb			*app = a->apr_next;
15286091Sjhb			return 0;
15386091Sjhb		}
15486091Sjhb	return -1;
15586091Sjhb}
15686091Sjhb
15786091Sjhb
15886091Sjhbint appr_ok(ip, tcp, nat)
15986091Sjhbip_t *ip;
16086091Sjhbtcphdr_t *tcp;
16186091Sjhbipnat_t *nat;
162227389Sjhb{
16386091Sjhb	aproxy_t *apr = nat->in_apr;
16486091Sjhb	u_short dport = nat->in_dport;
16586091Sjhb
16686091Sjhb	if (!apr || (apr->apr_flags & APR_DELETE) ||
16786091Sjhb	    (ip->ip_p != apr->apr_p))
16886091Sjhb		return 0;
16986091Sjhb	if ((tcp && (tcp->th_dport != dport)) || (!tcp && dport))
17086091Sjhb		return 0;
17186091Sjhb	return 1;
17286091Sjhb}
17386091Sjhb
17486091Sjhb
17586091Sjhb/*
176189749Sjhb * Allocate a new application proxy structure and fill it in with the
17786091Sjhb * relevant details.  call the init function once complete, prior to
178189749Sjhb * returning.
17986091Sjhb */
18086091Sjhbstatic ap_session_t *appr_new_session(apr, ip, fin, nat)
18186091Sjhbaproxy_t *apr;
18286091Sjhbip_t *ip;
18386091Sjhbfr_info_t *fin;
18486091Sjhbnat_t *nat;
18586091Sjhb{
18686091Sjhb	register ap_session_t *aps;
18786091Sjhb
18886091Sjhb	if (!apr || (apr->apr_flags & APR_DELETE) || (ip->ip_p != apr->apr_p))
18986091Sjhb		return NULL;
19086091Sjhb
19186091Sjhb	KMALLOC(aps, ap_session_t *);
19286091Sjhb	if (!aps)
19386091Sjhb		return NULL;
19486091Sjhb	bzero((char *)aps, sizeof(*aps));
19586091Sjhb	aps->aps_p = ip->ip_p;
19686091Sjhb	aps->aps_data = NULL;
19786091Sjhb	aps->aps_apr = apr;
198163897Smarcel	aps->aps_psiz = 0;
19986091Sjhb	if (apr->apr_new != NULL)
20086091Sjhb		if ((*apr->apr_new)(fin, ip, aps, nat) == -1) {
20186091Sjhb			KFREE(aps);
20286091Sjhb			return NULL;
20386091Sjhb		}
20486091Sjhb	aps->aps_nat = nat;
20586091Sjhb	aps->aps_next = ap_sess_list;
20686091Sjhb	ap_sess_list = aps;
20786091Sjhb	return aps;
20886091Sjhb}
20986091Sjhb
21086091Sjhb
21186091Sjhb/*
21286091Sjhb * check to see if a packet should be passed through an active proxy routine
21386091Sjhb * if one has been setup for it.
21486091Sjhb */
21586091Sjhbint appr_check(ip, fin, nat)
21686091Sjhbip_t *ip;
21786091Sjhbfr_info_t *fin;
21886091Sjhbnat_t *nat;
21986091Sjhb{
22086091Sjhb	ap_session_t *aps;
22186091Sjhb	aproxy_t *apr;
22286091Sjhb	tcphdr_t *tcp = NULL;
22386091Sjhb	u_32_t sum;
22486091Sjhb	short rv;
22586091Sjhb	int err;
22686091Sjhb
22786091Sjhb	if (nat->nat_aps == NULL)
22886091Sjhb		nat->nat_aps = appr_new_session(nat->nat_ptr->in_apr, ip,
22986091Sjhb						fin, nat);
23086091Sjhb	aps = nat->nat_aps;
23186091Sjhb	if ((aps != NULL) && (aps->aps_p == ip->ip_p)) {
23286091Sjhb		if (ip->ip_p == IPPROTO_TCP) {
233163897Smarcel			tcp = (tcphdr_t *)fin->fin_dp;
23486091Sjhb			/*
23586091Sjhb			 * verify that the checksum is correct.  If not, then
23686091Sjhb			 * don't do anything with this packet.
23786091Sjhb			 */
238189749Sjhb#if SOLARIS && defined(_KERNEL)
23986091Sjhb			sum = fr_tcpsum(fin->fin_qfm, ip, tcp);
24086091Sjhb#else
24186091Sjhb			sum = fr_tcpsum(*(mb_t **)fin->fin_mp, ip, tcp);
24286091Sjhb#endif
24386091Sjhb			if (sum != tcp->th_sum) {
24486091Sjhb				frstats[fin->fin_out].fr_tcpbad++;
24586091Sjhb				return -1;
24686091Sjhb			}
247189749Sjhb		}
24886091Sjhb
249189749Sjhb		apr = aps->aps_apr;
25086091Sjhb		err = 0;
25186091Sjhb		if (fin->fin_out != 0) {
25286091Sjhb			if (apr->apr_outpkt != NULL)
25386091Sjhb				err = (*apr->apr_outpkt)(fin, ip, aps, nat);
25486091Sjhb		} else {
25586091Sjhb			if (apr->apr_inpkt != NULL)
25686091Sjhb				err = (*apr->apr_inpkt)(fin, ip, aps, nat);
25786091Sjhb		}
25886091Sjhb
25986091Sjhb		rv = APR_EXIT(err);
260189749Sjhb		if (rv == -1)
261189749Sjhb			return rv;
262189749Sjhb
26386091Sjhb		if (tcp != NULL) {
26486091Sjhb			err = appr_fixseqack(fin, ip, aps, APR_INC(err));
26586091Sjhb#if SOLARIS && defined(_KERNEL)
266189749Sjhb			tcp->th_sum = fr_tcpsum(fin->fin_qfm, ip, tcp);
267189749Sjhb#else
268189749Sjhb			tcp->th_sum = fr_tcpsum(*(mb_t **)fin->fin_mp, ip, tcp);
26986091Sjhb#endif
27086091Sjhb		}
27186091Sjhb		aps->aps_bytes += ip->ip_len;
27286091Sjhb		aps->aps_pkts++;
27386091Sjhb		return 1;
27486091Sjhb	}
27586091Sjhb	return 0;
27686091Sjhb}
27786091Sjhb
27886091Sjhb
27986091Sjhbaproxy_t *appr_match(pr, name)
28086091Sjhbu_int pr;
28186091Sjhbchar *name;
282189749Sjhb{
283189749Sjhb	aproxy_t *ap;
284189749Sjhb
285189749Sjhb	for (ap = ap_proxies; ap->apr_p; ap++)
286189749Sjhb		if ((ap->apr_p == pr) &&
287189749Sjhb		    !strncmp(name, ap->apr_label, sizeof(ap->apr_label))) {
288189749Sjhb			ap->apr_ref++;
289189749Sjhb			return ap;
290189749Sjhb		}
291189749Sjhb
292189749Sjhb	for (ap = ap_proxylist; ap; ap = ap->apr_next)
293189749Sjhb		if ((ap->apr_p == pr) &&
294189749Sjhb		    !strncmp(name, ap->apr_label, sizeof(ap->apr_label))) {
295189749Sjhb			ap->apr_ref++;
296189749Sjhb			return ap;
29786091Sjhb		}
298189749Sjhb	return NULL;
299189749Sjhb}
300189749Sjhb
301189749Sjhb
302189749Sjhbvoid appr_free(ap)
303189749Sjhbaproxy_t *ap;
304189749Sjhb{
305189749Sjhb	ap->apr_ref--;
306189749Sjhb}
307189749Sjhb
308189749Sjhb
309189749Sjhbvoid aps_free(aps)
310189749Sjhbap_session_t *aps;
311189749Sjhb{
312189749Sjhb	ap_session_t *a, **ap;
313189749Sjhb
314189749Sjhb	if (!aps)
315189749Sjhb		return;
316189749Sjhb
317189749Sjhb	for (ap = &ap_sess_list; (a = *ap); ap = &a->aps_next)
318189749Sjhb		if (a == aps) {
319189749Sjhb			*ap = a->aps_next;
320189749Sjhb			break;
321189749Sjhb		}
322189749Sjhb
323189749Sjhb	if ((aps->aps_data != NULL) && (aps->aps_psiz != 0))
324226748Sjhb		KFREES(aps->aps_data, aps->aps_psiz);
325189749Sjhb	KFREE(aps);
326226748Sjhb}
327189749Sjhb
328189749Sjhb
32986091Sjhbstatic int appr_fixseqack(fin, ip, aps, inc)
33086091Sjhbfr_info_t *fin;
331189749Sjhbip_t *ip;
33286091Sjhbap_session_t *aps;
333189749Sjhbint inc;
334189749Sjhb{
33586091Sjhb	int sel, ch = 0, out, nlen;
336226746Sjhb	u_32_t seq1, seq2;
337189749Sjhb	tcphdr_t *tcp;
338189749Sjhb
33986091Sjhb	tcp = (tcphdr_t *)fin->fin_dp;
34086091Sjhb	out = fin->fin_out;
34186091Sjhb	nlen = ip->ip_len;
342189749Sjhb	nlen -= (ip->ip_hl << 2) + (tcp->th_off << 2);
34386091Sjhb
344189749Sjhb	if (out != 0) {
345189749Sjhb		seq1 = (u_32_t)ntohl(tcp->th_seq);
346189749Sjhb		sel = aps->aps_sel[out];
347189749Sjhb
348189749Sjhb		/* switch to other set ? */
349189749Sjhb		if ((aps->aps_seqmin[!sel] > aps->aps_seqmin[sel]) &&
350189749Sjhb		    (seq1 > aps->aps_seqmin[!sel]))
351189749Sjhb			sel = aps->aps_sel[out] = !sel;
352189749Sjhb
35386091Sjhb		if (aps->aps_seqoff[sel]) {
35486091Sjhb			seq2 = aps->aps_seqmin[sel] - aps->aps_seqoff[sel];
35586091Sjhb			if (seq1 > seq2) {
35686091Sjhb				seq2 = aps->aps_seqoff[sel];
35786091Sjhb				seq1 += seq2;
35886091Sjhb				tcp->th_seq = htonl(seq1);
359130603Sphk				ch = 1;
36086091Sjhb			}
36186091Sjhb		}
36286091Sjhb
36386091Sjhb		if (inc && (seq1 > aps->aps_seqmin[!sel])) {
36486091Sjhb			aps->aps_seqmin[!sel] = seq1 + nlen - 1;
36586091Sjhb			aps->aps_seqoff[!sel] = aps->aps_seqoff[sel] + inc;
36686091Sjhb		}
36786091Sjhb
368163897Smarcel		/***/
36986091Sjhb
37086091Sjhb		seq1 = ntohl(tcp->th_ack);
37186091Sjhb		sel = aps->aps_sel[1 - out];
37286091Sjhb
37386091Sjhb		/* switch to other set ? */
37486091Sjhb		if ((aps->aps_ackmin[!sel] > aps->aps_ackmin[sel]) &&
37586091Sjhb		    (seq1 > aps->aps_ackmin[!sel]))
37686091Sjhb			sel = aps->aps_sel[1 - out] = !sel;
37786091Sjhb
378130603Sphk		if (aps->aps_ackoff[sel] && (seq1 > aps->aps_ackmin[sel])) {
37986091Sjhb			seq2 = aps->aps_ackoff[sel];
38086091Sjhb			tcp->th_ack = htonl(seq1 - seq2);
38186091Sjhb			ch = 1;
38286091Sjhb		}
38386091Sjhb	} else {
38486091Sjhb		seq1 = ntohl(tcp->th_seq);
38586091Sjhb		sel = aps->aps_sel[out];
38686091Sjhb
387172921Sjhb		/* switch to other set ? */
38886091Sjhb		if ((aps->aps_ackmin[!sel] > aps->aps_ackmin[sel]) &&
38986091Sjhb		    (seq1 > aps->aps_ackmin[!sel]))
39086091Sjhb			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