1/* $FreeBSD: stable/11/sys/contrib/ipfilter/netinet/ip_rcmd_pxy.c 369245 2021-02-09 13:47:46Z git2svn $ */ 2 3/* 4 * Copyright (C) 2012 by Darren Reed. 5 * 6 * See the IPFILTER.LICENCE file for details on licencing. 7 * 8 * $Id$ 9 * 10 * Simple RCMD transparent proxy for in-kernel use. For use with the NAT 11 * code. 12 * $FreeBSD: stable/11/sys/contrib/ipfilter/netinet/ip_rcmd_pxy.c 369245 2021-02-09 13:47:46Z git2svn $ 13 */ 14 15#define IPF_RCMD_PROXY 16 17typedef struct rcmdinfo { 18 u_32_t rcmd_port; /* Port number seen */ 19 u_32_t rcmd_portseq; /* Sequence number where port is first seen */ 20 ipnat_t *rcmd_rule; /* Template rule for back connection */ 21} rcmdinfo_t; 22 23void ipf_p_rcmd_main_load(void); 24void ipf_p_rcmd_main_unload(void); 25 26int ipf_p_rcmd_init(void); 27void ipf_p_rcmd_fini(void); 28void ipf_p_rcmd_del(ipf_main_softc_t *, ap_session_t *); 29int ipf_p_rcmd_new(void *, fr_info_t *, ap_session_t *, nat_t *); 30int ipf_p_rcmd_out(void *, fr_info_t *, ap_session_t *, nat_t *); 31int ipf_p_rcmd_in(void *, fr_info_t *, ap_session_t *, nat_t *); 32u_short ipf_rcmd_atoi(char *); 33int ipf_p_rcmd_portmsg(fr_info_t *, ap_session_t *, nat_t *); 34 35static frentry_t rcmdfr; 36 37static int rcmd_proxy_init = 0; 38 39 40/* 41 * RCMD application proxy initialization. 42 */ 43void 44ipf_p_rcmd_main_load() 45{ 46 bzero((char *)&rcmdfr, sizeof(rcmdfr)); 47 rcmdfr.fr_ref = 1; 48 rcmdfr.fr_flags = FR_INQUE|FR_PASS|FR_QUICK|FR_KEEPSTATE; 49 MUTEX_INIT(&rcmdfr.fr_lock, "RCMD proxy rule lock"); 50 rcmd_proxy_init = 1; 51} 52 53 54void 55ipf_p_rcmd_main_unload() 56{ 57 if (rcmd_proxy_init == 1) { 58 MUTEX_DESTROY(&rcmdfr.fr_lock); 59 rcmd_proxy_init = 0; 60 } 61} 62 63 64/* 65 * Setup for a new RCMD proxy. 66 */ 67int 68ipf_p_rcmd_new(arg, fin, aps, nat) 69 void *arg; 70 fr_info_t *fin; 71 ap_session_t *aps; 72 nat_t *nat; 73{ 74 tcphdr_t *tcp = (tcphdr_t *)fin->fin_dp; 75 rcmdinfo_t *rc; 76 ipnat_t *ipn; 77 ipnat_t *np; 78 int size; 79 80 fin = fin; /* LINT */ 81 82 np = nat->nat_ptr; 83 size = np->in_size; 84 KMALLOC(rc, rcmdinfo_t *); 85 if (rc == NULL) { 86#ifdef IP_RCMD_PROXY_DEBUG 87 printf("ipf_p_rcmd_new:KMALLOCS(%d) failed\n", sizeof(*rc)); 88#endif 89 return -1; 90 } 91 aps->aps_sport = tcp->th_sport; 92 aps->aps_dport = tcp->th_dport; 93 94 ipn = ipf_proxy_rule_rev(nat); 95 if (ipn == NULL) { 96 KFREE(rc); 97 return -1; 98 } 99 100 aps->aps_data = rc; 101 aps->aps_psiz = sizeof(*rc); 102 bzero((char *)rc, sizeof(*rc)); 103 104 rc->rcmd_rule = ipn; 105 106 return 0; 107} 108 109 110void 111ipf_p_rcmd_del(softc, aps) 112 ipf_main_softc_t *softc; 113 ap_session_t *aps; 114{ 115 rcmdinfo_t *rci; 116 117 rci = aps->aps_data; 118 if (rci != NULL) { 119 rci->rcmd_rule->in_flags |= IPN_DELETE; 120 ipf_nat_rule_deref(softc, &rci->rcmd_rule); 121 } 122} 123 124 125/* 126 * ipf_rcmd_atoi - implement a simple version of atoi 127 */ 128u_short 129ipf_rcmd_atoi(ptr) 130 char *ptr; 131{ 132 register char *s = ptr, c; 133 register u_short i = 0; 134 135 while (((c = *s++) != '\0') && ISDIGIT(c)) { 136 i *= 10; 137 i += c - '0'; 138 } 139 return i; 140} 141 142 143int 144ipf_p_rcmd_portmsg(fin, aps, nat) 145 fr_info_t *fin; 146 ap_session_t *aps; 147 nat_t *nat; 148{ 149 tcphdr_t *tcp, tcph, *tcp2 = &tcph; 150 int off, dlen, nflags, direction; 151 ipf_main_softc_t *softc; 152 ipf_nat_softc_t *softn; 153 char portbuf[8], *s; 154 rcmdinfo_t *rc; 155 fr_info_t fi; 156 u_short sp; 157 nat_t *nat2; 158#ifdef USE_INET6 159 ip6_t *ip6; 160#endif 161 int tcpsz; 162 int slen = 0; /* silence gcc */ 163 ip_t *ip; 164 mb_t *m; 165 166 tcp = (tcphdr_t *)fin->fin_dp; 167 168 m = fin->fin_m; 169 ip = fin->fin_ip; 170 tcpsz = TCP_OFF(tcp) << 2; 171#ifdef USE_INET6 172 ip6 = (ip6_t *)fin->fin_ip; 173#endif 174 softc = fin->fin_main_soft; 175 softn = softc->ipf_nat_soft; 176 off = (char *)tcp - (char *)ip + tcpsz + fin->fin_ipoff; 177 178 dlen = fin->fin_dlen - tcpsz; 179 if (dlen <= 0) 180 return 0; 181 182 rc = (rcmdinfo_t *)aps->aps_data; 183 if ((rc->rcmd_portseq != 0) && 184 (tcp->th_seq != rc->rcmd_portseq)) 185 return 0; 186 187 bzero(portbuf, sizeof(portbuf)); 188 COPYDATA(m, off, MIN(sizeof(portbuf), dlen), portbuf); 189 190 portbuf[sizeof(portbuf) - 1] = '\0'; 191 s = portbuf; 192 sp = ipf_rcmd_atoi(s); 193 if (sp == 0) { 194#ifdef IP_RCMD_PROXY_DEBUG 195 printf("ipf_p_rcmd_portmsg:sp == 0 dlen %d [%s]\n", 196 dlen, portbuf); 197#endif 198 return 0; 199 } 200 201 if (rc->rcmd_port != 0 && sp != rc->rcmd_port) { 202#ifdef IP_RCMD_PROXY_DEBUG 203 printf("ipf_p_rcmd_portmsg:sp(%d) != rcmd_port(%d)\n", 204 sp, rc->rcmd_port); 205#endif 206 return 0; 207 } 208 209 rc->rcmd_port = sp; 210 rc->rcmd_portseq = tcp->th_seq; 211 212 /* 213 * Initialise the packet info structure so we can search the NAT 214 * table to see if there already is soemthing present that matches 215 * up with what we want to add. 216 */ 217 bcopy((char *)fin, (char *)&fi, sizeof(fi)); 218 fi.fin_flx |= FI_IGNORE; 219 fi.fin_data[0] = 0; 220 fi.fin_data[1] = sp; 221 fi.fin_src6 = nat->nat_ndst6; 222 fi.fin_dst6 = nat->nat_nsrc6; 223 224 if (nat->nat_v[0] == 6) { 225#ifdef USE_INET6 226 if (nat->nat_dir == NAT_OUTBOUND) { 227 nat2 = ipf_nat6_outlookup(&fi, NAT_SEARCH|IPN_TCP, 228 nat->nat_pr[1], 229 &nat->nat_osrc6.in6, 230 &nat->nat_odst6.in6); 231 } else { 232 nat2 = ipf_nat6_inlookup(&fi, NAT_SEARCH|IPN_TCP, 233 nat->nat_pr[0], 234 &nat->nat_osrc6.in6, 235 &nat->nat_odst6.in6); 236 } 237#else 238 nat2 = (void *)-1; 239#endif 240 } else { 241 if (nat->nat_dir == NAT_OUTBOUND) { 242 nat2 = ipf_nat_outlookup(&fi, NAT_SEARCH|IPN_TCP, 243 nat->nat_pr[1], 244 nat->nat_osrcip, 245 nat->nat_odstip); 246 } else { 247 nat2 = ipf_nat_inlookup(&fi, NAT_SEARCH|IPN_TCP, 248 nat->nat_pr[0], 249 nat->nat_osrcip, 250 nat->nat_odstip); 251 } 252 } 253 if (nat2 != NULL) 254 return APR_ERR(1); 255 256 /* 257 * Add skeleton NAT entry for connection which will come 258 * back the other way. 259 */ 260 261 if (nat->nat_v[0] == 6) { 262#ifdef USE_INET6 263 slen = ip6->ip6_plen; 264 ip6->ip6_plen = htons(sizeof(*tcp)); 265#endif 266 } else { 267 slen = ip->ip_len; 268 ip->ip_len = htons(fin->fin_hlen + sizeof(*tcp)); 269 } 270 271 /* 272 * Fill out the fake TCP header with a few fields that ipfilter 273 * considers to be important. 274 */ 275 bzero((char *)tcp2, sizeof(*tcp2)); 276 tcp2->th_win = htons(8192); 277 TCP_OFF_A(tcp2, 5); 278 tcp2->th_flags = TH_SYN; 279 280 fi.fin_dp = (char *)tcp2; 281 fi.fin_fr = &rcmdfr; 282 fi.fin_dlen = sizeof(*tcp2); 283 fi.fin_plen = fi.fin_hlen + sizeof(*tcp2); 284 fi.fin_flx &= FI_LOWTTL|FI_FRAG|FI_TCPUDP|FI_OPTIONS|FI_IGNORE; 285 286 if (nat->nat_dir == NAT_OUTBOUND) { 287 fi.fin_out = 0; 288 direction = NAT_INBOUND; 289 } else { 290 fi.fin_out = 1; 291 direction = NAT_OUTBOUND; 292 } 293 nflags = SI_W_SPORT|NAT_SLAVE|IPN_TCP; 294 295 MUTEX_ENTER(&softn->ipf_nat_new); 296 if (fin->fin_v == 4) 297 nat2 = ipf_nat_add(&fi, rc->rcmd_rule, NULL, nflags, 298 direction); 299#ifdef USE_INET6 300 else 301 nat2 = ipf_nat6_add(&fi, rc->rcmd_rule, NULL, nflags, 302 direction); 303#endif 304 MUTEX_EXIT(&softn->ipf_nat_new); 305 306 if (nat2 != NULL) { 307 (void) ipf_nat_proto(&fi, nat2, IPN_TCP); 308 MUTEX_ENTER(&nat2->nat_lock); 309 ipf_nat_update(&fi, nat2); 310 MUTEX_EXIT(&nat2->nat_lock); 311 fi.fin_ifp = NULL; 312 if (nat2->nat_dir == NAT_INBOUND) 313 fi.fin_dst6 = nat->nat_osrc6; 314 (void) ipf_state_add(softc, &fi, NULL, SI_W_SPORT); 315 } 316 if (nat->nat_v[0] == 6) { 317#ifdef USE_INET6 318 ip6->ip6_plen = slen; 319#endif 320 } else { 321 ip->ip_len = slen; 322 } 323 if (nat2 == NULL) 324 return APR_ERR(1); 325 return 0; 326} 327 328 329int 330ipf_p_rcmd_out(arg, fin, aps, nat) 331 void *arg; 332 fr_info_t *fin; 333 ap_session_t *aps; 334 nat_t *nat; 335{ 336 if (nat->nat_dir == NAT_OUTBOUND) 337 return ipf_p_rcmd_portmsg(fin, aps, nat); 338 return 0; 339} 340 341 342int 343ipf_p_rcmd_in(arg, fin, aps, nat) 344 void *arg; 345 fr_info_t *fin; 346 ap_session_t *aps; 347 nat_t *nat; 348{ 349 if (nat->nat_dir == NAT_INBOUND) 350 return ipf_p_rcmd_portmsg(fin, aps, nat); 351 return 0; 352} 353