1 /* 2 * Copyright 2001, QNX Software Systems Ltd. All Rights Reserved 3 * 4 * This source code has been published by QNX Software Systems Ltd. (QSSL). 5 * However, any use, reproduction, modification, distribution or transfer of 6 * this software, or any software which includes or is based upon any of this 7 * code, is only permitted under the terms of the QNX Open Community License 8 * version 1.0 (see licensing.qnx.com for details) or as otherwise expressly 9 * authorized by a written license agreement from QSSL. For more information, 10 * please email licensing@qnx.com. 11 * 12 * For more details, see QNX_OCL.txt provided with this distribution. 13 * 14 * Copyright 2007 Sun Microsystems, Inc. All rights reserved. 15 * Use is subject to license terms. 16 */ 17 18 /* 19 * Simple H.323 proxy 20 * 21 * by xtang@canada.com 22 * ported to ipfilter 3.4.20 by Michael Grant mg-ipf@grant.org 23 */ 24 25 #pragma ident "%Z%%M% %I% %E% SMI" 26 27 #if __FreeBSD_version >= 220000 && defined(_KERNEL) 28 # include <sys/fcntl.h> 29 # include <sys/filio.h> 30 #else 31 # ifndef linux 32 # include <sys/ioctl.h> 33 # endif 34 #endif 35 36 #define IPF_H323_PROXY 37 38 typedef struct ifs_h323pxy { 39 frentry_t h323_fr; 40 int h323_proxy_init; 41 } ifs_h323pxy_t; 42 43 int ippr_h323_init __P((void **, ipf_stack_t *)); 44 void ippr_h323_fini __P((void **, ipf_stack_t *)); 45 int ippr_h323_new __P((fr_info_t *, ap_session_t *, nat_t *, void *)); 46 void ippr_h323_del __P((ap_session_t *, void *, ipf_stack_t *)); 47 int ippr_h323_out __P((fr_info_t *, ap_session_t *, nat_t *, void *)); 48 int ippr_h323_in __P((fr_info_t *, ap_session_t *, nat_t *, void *)); 49 50 int ippr_h245_new __P((fr_info_t *, ap_session_t *, nat_t *, void *)); 51 int ippr_h245_out __P((fr_info_t *, ap_session_t *, nat_t *, void *)); 52 int ippr_h245_in __P((fr_info_t *, ap_session_t *, nat_t *, void *)); 53 54 static int find_port __P((int, caddr_t, int datlen, int *, u_short *)); 55 56 57 static int find_port(ipaddr, data, datlen, off, port) 58 int ipaddr; 59 caddr_t data; 60 int datlen, *off; 61 unsigned short *port; 62 { 63 u_32_t addr, netaddr; 64 u_char *dp; 65 int offset; 66 67 if (datlen < 6) 68 return -1; 69 70 *port = 0; 71 offset = *off; 72 dp = (u_char *)data; 73 netaddr = ntohl(ipaddr); 74 75 for (offset = 0; offset <= datlen - 6; offset++, dp++) { 76 addr = (dp[0] << 24) | (dp[1] << 16) | (dp[2] << 8) | dp[3]; 77 if (netaddr == addr) 78 { 79 *port = (*(dp + 4) << 8) | *(dp + 5); 80 break; 81 } 82 } 83 *off = offset; 84 return (offset > datlen - 6) ? -1 : 0; 85 } 86 87 /* 88 * Initialize local structures. 89 */ 90 /*ARGSUSED*/ 91 int ippr_h323_init(private, ifs) 92 void **private; 93 ipf_stack_t *ifs; 94 { 95 ifs_h323pxy_t *ifsh323; 96 97 KMALLOC(ifsh323, ifs_h323pxy_t *); 98 if (ifsh323 == NULL) 99 return -1; 100 101 ifsh323->h323_fr.fr_ref = 1; 102 ifsh323->h323_fr.fr_flags = FR_INQUE|FR_PASS|FR_QUICK|FR_KEEPSTATE; 103 MUTEX_INIT(&ifsh323->h323_fr.fr_lock, "H323 proxy rule lock"); 104 ifsh323->h323_proxy_init = 1; 105 106 *private = (void *)ifsh323; 107 108 return 0; 109 } 110 111 112 /*ARGSUSED*/ 113 void ippr_h323_fini(private, ifs) 114 void **private; 115 ipf_stack_t *ifs; 116 { 117 ifs_h323pxy_t *ifsh323 = *((ifs_h323pxy_t **)private); 118 119 if (ifsh323->h323_proxy_init == 1) { 120 MUTEX_DESTROY(&ifsh323->h323_fr.fr_lock); 121 ifsh323->h323_proxy_init = 0; 122 } 123 124 KFREE(ifsh323); 125 *private = NULL; 126 } 127 128 /*ARGSUSED*/ 129 int ippr_h323_new(fin, aps, nat, private) 130 fr_info_t *fin; 131 ap_session_t *aps; 132 nat_t *nat; 133 void *private; 134 { 135 fin = fin; /* LINT */ 136 nat = nat; /* LINT */ 137 138 aps->aps_data = NULL; 139 aps->aps_psiz = 0; 140 141 return 0; 142 } 143 144 /*ARGSUSED*/ 145 void ippr_h323_del(aps, private, ifs) 146 ap_session_t *aps; 147 void *private; 148 ipf_stack_t *ifs; 149 { 150 int i; 151 ipnat_t *ipn; 152 153 if (aps->aps_data) { 154 for (i = 0, ipn = aps->aps_data; 155 i < (aps->aps_psiz / sizeof(ipnat_t)); 156 i++, ipn = (ipnat_t *)((char *)ipn + sizeof(*ipn))) 157 { 158 /* 159 * Check the comment in ippr_h323_in() function, 160 * just above fr_nat_ioctl() call. 161 * We are lucky here because this function is not 162 * called with ipf_nat locked. 163 */ 164 if (fr_nat_ioctl((caddr_t)ipn, SIOCRMNAT, NAT_SYSSPACE| 165 NAT_LOCKHELD|FWRITE, 0, NULL, ifs) == -1) { 166 /*EMPTY*/; 167 /* log the error */ 168 } 169 } 170 KFREES(aps->aps_data, aps->aps_psiz); 171 /* avoid double free */ 172 aps->aps_data = NULL; 173 aps->aps_psiz = 0; 174 } 175 return; 176 } 177 178 179 /*ARGSUSED*/ 180 int ippr_h323_in(fin, aps, nat, private) 181 fr_info_t *fin; 182 ap_session_t *aps; 183 nat_t *nat; 184 void *private; 185 { 186 int ipaddr, off, datlen; 187 unsigned short port; 188 caddr_t data; 189 tcphdr_t *tcp; 190 ip_t *ip; 191 ipf_stack_t *ifs = fin->fin_ifs; 192 193 ip = fin->fin_ip; 194 tcp = (tcphdr_t *)fin->fin_dp; 195 ipaddr = ip->ip_src.s_addr; 196 197 data = (caddr_t)tcp + (TCP_OFF(tcp) << 2); 198 datlen = fin->fin_dlen - (TCP_OFF(tcp) << 2); 199 if (find_port(ipaddr, data, datlen, &off, &port) == 0) { 200 ipnat_t *ipn; 201 char *newarray; 202 203 /* setup a nat rule to set a h245 proxy on tcp-port "port" 204 * it's like: 205 * map <if> <inter_ip>/<mask> -> <gate_ip>/<mask> proxy port <port> <port>/tcp 206 */ 207 KMALLOCS(newarray, char *, aps->aps_psiz + sizeof(*ipn)); 208 if (newarray == NULL) { 209 return -1; 210 } 211 ipn = (ipnat_t *)&newarray[aps->aps_psiz]; 212 bcopy((caddr_t)nat->nat_ptr, (caddr_t)ipn, sizeof(ipnat_t)); 213 (void) strncpy(ipn->in_plabel, "h245", APR_LABELLEN); 214 215 ipn->in_inip = nat->nat_inip.s_addr; 216 ipn->in_inmsk = 0xffffffff; 217 ipn->in_dport = htons(port); 218 /* 219 * we got a problem here. we need to call fr_nat_ioctl() to add 220 * the h245 proxy rule, but since we already hold (READ locked) 221 * the nat table rwlock (ipf_nat), if we go into fr_nat_ioctl(), 222 * it will try to WRITE lock it. This will causing dead lock 223 * on RTP. 224 * 225 * The quick & dirty solution here is release the read lock, 226 * call fr_nat_ioctl() and re-lock it. 227 * A (maybe better) solution is do a UPGRADE(), and instead 228 * of calling fr_nat_ioctl(), we add the nat rule ourself. 229 */ 230 RWLOCK_EXIT(&ifs->ifs_ipf_nat); 231 if (fr_nat_ioctl((caddr_t)ipn, SIOCADNAT, 232 NAT_SYSSPACE|FWRITE, 0, NULL, ifs) == -1) { 233 READ_ENTER(&ifs->ifs_ipf_nat); 234 return -1; 235 } 236 READ_ENTER(&ifs->ifs_ipf_nat); 237 if (aps->aps_data != NULL && aps->aps_psiz > 0) { 238 bcopy(aps->aps_data, newarray, aps->aps_psiz); 239 KFREES(aps->aps_data, aps->aps_psiz); 240 } 241 aps->aps_data = newarray; 242 aps->aps_psiz += sizeof(*ipn); 243 } 244 return 0; 245 } 246 247 248 /*ARGSUSED*/ 249 int ippr_h245_new(fin, aps, nat, private) 250 fr_info_t *fin; 251 ap_session_t *aps; 252 nat_t *nat; 253 void *private; 254 { 255 fin = fin; /* LINT */ 256 nat = nat; /* LINT */ 257 258 aps->aps_data = NULL; 259 aps->aps_psiz = 0; 260 return 0; 261 } 262 263 264 /*ARGSUSED*/ 265 int ippr_h245_out(fin, aps, nat, private) 266 fr_info_t *fin; 267 ap_session_t *aps; 268 nat_t *nat; 269 void *private; 270 { 271 int ipaddr, off, datlen; 272 tcphdr_t *tcp; 273 caddr_t data; 274 u_short port; 275 ip_t *ip; 276 ipf_stack_t *ifs = fin->fin_ifs; 277 278 aps = aps; /* LINT */ 279 280 ip = fin->fin_ip; 281 tcp = (tcphdr_t *)fin->fin_dp; 282 ipaddr = nat->nat_inip.s_addr; 283 data = (caddr_t)tcp + (TCP_OFF(tcp) << 2); 284 datlen = fin->fin_dlen - (TCP_OFF(tcp) << 2); 285 if (find_port(ipaddr, data, datlen, &off, &port) == 0) { 286 fr_info_t fi; 287 nat_t *nat2; 288 289 /* port = htons(port); */ 290 nat2 = nat_outlookup(fin->fin_ifp, IPN_UDP, IPPROTO_UDP, 291 ip->ip_src, ip->ip_dst); 292 if (nat2 == NULL) { 293 struct ip newip; 294 struct udphdr udp; 295 296 bcopy((caddr_t)ip, (caddr_t)&newip, sizeof(newip)); 297 newip.ip_len = fin->fin_hlen + sizeof(udp); 298 newip.ip_p = IPPROTO_UDP; 299 newip.ip_src = nat->nat_inip; 300 301 bzero((char *)&udp, sizeof(udp)); 302 udp.uh_sport = port; 303 304 bcopy((caddr_t)fin, (caddr_t)&fi, sizeof(fi)); 305 fi.fin_fi.fi_p = IPPROTO_UDP; 306 fi.fin_data[0] = port; 307 fi.fin_data[1] = 0; 308 fi.fin_dp = (char *)&udp; 309 310 nat2 = nat_new(&fi, nat->nat_ptr, NULL, 311 NAT_SLAVE|IPN_UDP|SI_W_DPORT, 312 NAT_OUTBOUND); 313 if (nat2 != NULL) { 314 (void) nat_proto(&fi, nat2, IPN_UDP); 315 nat_update(&fi, nat2, nat2->nat_ptr); 316 317 nat2->nat_ptr->in_hits++; 318 #ifdef IPFILTER_LOG 319 nat_log(nat2, (u_int)(nat->nat_ptr->in_redir), 320 ifs); 321 #endif 322 bcopy((caddr_t)&ip->ip_src.s_addr, 323 data + off, 4); 324 bcopy((caddr_t)&nat2->nat_outport, 325 data + off + 4, 2); 326 } 327 } 328 } 329 return 0; 330 } 331