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