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