1 /* 2 * Copyright (C) 1998-2003 by Darren Reed 3 * 4 * See the IPFILTER.LICENCE file for details on licencing. 5 * 6 * $Id: ip_rcmd_pxy.c,v 1.41.2.4 2005/02/04 10:22:55 darrenr Exp $ 7 * 8 * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 9 * Use is subject to license terms. 10 * 11 * Simple RCMD transparent proxy for in-kernel use. For use with the NAT 12 * code. 13 */ 14 15 #define IPF_RCMD_PROXY 16 17 typedef struct ifs_rcmdpxy { 18 frentry_t rcmdfr; 19 int rcmd_proxy_init; 20 } ifs_rcmdpxy_t; 21 22 int ippr_rcmd_init __P((void **, ipf_stack_t *)); 23 void ippr_rcmd_fini __P((void **, ipf_stack_t *)); 24 int ippr_rcmd_new __P((fr_info_t *, ap_session_t *, nat_t *, void *)); 25 int ippr_rcmd_out __P((fr_info_t *, ap_session_t *, nat_t *, void *)); 26 int ippr_rcmd_in __P((fr_info_t *, ap_session_t *, nat_t *, void *)); 27 u_short ipf_rcmd_atoi __P((char *)); 28 int ippr_rcmd_portmsg __P((fr_info_t *, ap_session_t *, nat_t *, ifs_rcmdpxy_t *)); 29 30 /* 31 * RCMD application proxy initialization. 32 */ 33 /*ARGSUSED*/ 34 int ippr_rcmd_init(private, ifs) 35 void **private; 36 ipf_stack_t *ifs; 37 { 38 ifs_rcmdpxy_t *ifsrcmd; 39 40 KMALLOC(ifsrcmd, ifs_rcmdpxy_t *); 41 if (ifsrcmd == NULL) 42 return -1; 43 44 bzero((char *)&ifsrcmd->rcmdfr, sizeof(ifsrcmd->rcmdfr)); 45 ifsrcmd->rcmdfr.fr_ref = 1; 46 ifsrcmd->rcmdfr.fr_flags = FR_INQUE|FR_PASS|FR_QUICK|FR_KEEPSTATE; 47 MUTEX_INIT(&ifsrcmd->rcmdfr.fr_lock, "RCMD proxy rule lock"); 48 ifsrcmd->rcmd_proxy_init = 1; 49 50 *private = (void *)ifsrcmd; 51 52 return 0; 53 } 54 55 56 /*ARGSUSED*/ 57 void ippr_rcmd_fini(private, ifs) 58 void **private; 59 ipf_stack_t *ifs; 60 { 61 ifs_rcmdpxy_t *ifsrcmd = *((ifs_rcmdpxy_t **)private); 62 63 if (ifsrcmd->rcmd_proxy_init == 1) { 64 MUTEX_DESTROY(&ifsrcmd->rcmdfr.fr_lock); 65 ifsrcmd->rcmd_proxy_init = 0; 66 } 67 68 KFREE(ifsrcmd); 69 *private = NULL; 70 } 71 72 73 /* 74 * Setup for a new RCMD proxy. 75 */ 76 /*ARGSUSED*/ 77 int ippr_rcmd_new(fin, aps, nat, private) 78 fr_info_t *fin; 79 ap_session_t *aps; 80 nat_t *nat; 81 void *private; 82 { 83 tcphdr_t *tcp = (tcphdr_t *)fin->fin_dp; 84 85 fin = fin; /* LINT */ 86 nat = nat; /* LINT */ 87 88 aps->aps_psiz = sizeof(u_32_t); 89 KMALLOCS(aps->aps_data, u_32_t *, sizeof(u_32_t)); 90 if (aps->aps_data == NULL) { 91 #ifdef IP_RCMD_PROXY_DEBUG 92 printf("ippr_rcmd_new:KMALLOCS(%d) failed\n", sizeof(u_32_t)); 93 #endif 94 return -1; 95 } 96 *(u_32_t *)aps->aps_data = 0; 97 aps->aps_sport = tcp->th_sport; 98 aps->aps_dport = tcp->th_dport; 99 return 0; 100 } 101 102 103 /* 104 * ipf_rcmd_atoi - implement a simple version of atoi 105 */ 106 u_short ipf_rcmd_atoi(ptr) 107 char *ptr; 108 { 109 register char *s = ptr, c; 110 register u_short i = 0; 111 112 while (((c = *s++) != '\0') && ISDIGIT(c)) { 113 i *= 10; 114 i += c - '0'; 115 } 116 return i; 117 } 118 119 120 int ippr_rcmd_portmsg(fin, aps, nat, ifsrcmd) 121 fr_info_t *fin; 122 ap_session_t *aps; 123 nat_t *nat; 124 ifs_rcmdpxy_t *ifsrcmd; 125 { 126 tcphdr_t *tcp, tcph, *tcp2 = &tcph; 127 struct in_addr swip, swip2; 128 int off, dlen, nflags; 129 char portbuf[8], *s; 130 fr_info_t fi; 131 u_short sp; 132 nat_t *nat2; 133 ip_t *ip; 134 mb_t *m; 135 136 tcp = (tcphdr_t *)fin->fin_dp; 137 138 if (tcp->th_flags & TH_SYN) { 139 *(u_32_t *)aps->aps_data = htonl(ntohl(tcp->th_seq) + 1); 140 return 0; 141 } 142 143 if ((*(u_32_t *)aps->aps_data != 0) && 144 (tcp->th_seq != *(u_32_t *)aps->aps_data)) 145 return 0; 146 147 m = fin->fin_m; 148 ip = fin->fin_ip; 149 off = (char *)tcp - (char *)ip + (TCP_OFF(tcp) << 2) + fin->fin_ipoff; 150 151 #ifdef __sgi 152 dlen = fin->fin_plen - off; 153 #else 154 dlen = MSGDSIZE(m) - off; 155 #endif 156 if (dlen <= 0) 157 return 0; 158 159 bzero(portbuf, sizeof(portbuf)); 160 COPYDATA(m, off, MIN(sizeof(portbuf), dlen), portbuf); 161 162 portbuf[sizeof(portbuf) - 1] = '\0'; 163 s = portbuf; 164 sp = ipf_rcmd_atoi(s); 165 if (sp == 0) { 166 #ifdef IP_RCMD_PROXY_DEBUG 167 printf("ippr_rcmd_portmsg:sp == 0 dlen %d [%s]\n", 168 dlen, portbuf); 169 #endif 170 return 0; 171 } 172 173 /* 174 * Add skeleton NAT entry for connection which will come back the 175 * other way. 176 */ 177 bcopy((char *)fin, (char *)&fi, sizeof(fi)); 178 fi.fin_flx |= FI_IGNORE; 179 fi.fin_data[0] = sp; 180 fi.fin_data[1] = 0; 181 if (nat->nat_dir == NAT_OUTBOUND) 182 nat2 = nat_outlookup(&fi, NAT_SEARCH|IPN_TCP, nat->nat_p, 183 nat->nat_inip, nat->nat_oip); 184 else 185 nat2 = nat_inlookup(&fi, NAT_SEARCH|IPN_TCP, nat->nat_p, 186 nat->nat_inip, nat->nat_oip); 187 if (nat2 == NULL) { 188 int slen; 189 190 slen = ip->ip_len; 191 ip->ip_len = fin->fin_hlen + sizeof(*tcp); 192 bzero((char *)tcp2, sizeof(*tcp2)); 193 tcp2->th_win = htons(8192); 194 tcp2->th_sport = htons(sp); 195 tcp2->th_dport = 0; /* XXX - don't specify remote port */ 196 TCP_OFF_A(tcp2, 5); 197 tcp2->th_flags = TH_SYN; 198 fi.fin_dp = (char *)tcp2; 199 fi.fin_fr = &ifsrcmd->rcmdfr; 200 fi.fin_dlen = sizeof(*tcp2); 201 fi.fin_plen = fi.fin_hlen + sizeof(*tcp2); 202 fi.fin_flx &= FI_LOWTTL|FI_FRAG|FI_TCPUDP|FI_OPTIONS|FI_IGNORE; 203 nflags = NAT_SLAVE|IPN_TCP|SI_W_DPORT; 204 205 swip = ip->ip_src; 206 swip2 = ip->ip_dst; 207 208 if (nat->nat_dir == NAT_OUTBOUND) { 209 fi.fin_fi.fi_saddr = nat->nat_inip.s_addr; 210 ip->ip_src = nat->nat_inip; 211 } else { 212 fi.fin_fi.fi_saddr = nat->nat_oip.s_addr; 213 ip->ip_src = nat->nat_oip; 214 nflags |= NAT_NOTRULEPORT; 215 } 216 217 nat2 = nat_new(&fi, nat->nat_ptr, NULL, nflags, nat->nat_dir); 218 219 if (nat2 != NULL) { 220 (void) nat_proto(&fi, nat2, IPN_TCP); 221 nat_update(&fi, nat2, nat2->nat_ptr); 222 fi.fin_ifp = NULL; 223 if (nat->nat_dir == NAT_INBOUND) { 224 fi.fin_fi.fi_daddr = nat->nat_inip.s_addr; 225 ip->ip_dst = nat->nat_inip; 226 } 227 (void) fr_addstate(&fi, &nat2->nat_state, SI_W_DPORT); 228 } 229 ip->ip_len = slen; 230 ip->ip_src = swip; 231 ip->ip_dst = swip2; 232 } 233 return 0; 234 } 235 236 237 int ippr_rcmd_out(fin, aps, nat, private) 238 fr_info_t *fin; 239 ap_session_t *aps; 240 nat_t *nat; 241 void *private; 242 { 243 if (nat->nat_dir == NAT_OUTBOUND) 244 return ippr_rcmd_portmsg(fin, aps, nat, (ifs_rcmdpxy_t *)private); 245 return 0; 246 } 247 248 249 int ippr_rcmd_in(fin, aps, nat, private) 250 fr_info_t *fin; 251 ap_session_t *aps; 252 nat_t *nat; 253 void *private; 254 { 255 if (nat->nat_dir == NAT_INBOUND) 256 return ippr_rcmd_portmsg(fin, aps, nat, (ifs_rcmdpxy_t *)private); 257 return 0; 258 } 259