1 1.3 darrenr /* $NetBSD: ip_ipsec_pxy.c,v 1.3 2012/07/22 14:27:51 darrenr Exp $ */ 2 1.1 christos 3 1.1 christos /* 4 1.3 darrenr * Copyright (C) 2012 by Darren Reed. 5 1.1 christos * 6 1.1 christos * See the IPFILTER.LICENCE file for details on licencing. 7 1.1 christos * 8 1.1 christos * Simple ISAKMP transparent proxy for in-kernel use. For use with the NAT 9 1.1 christos * code. 10 1.1 christos * 11 1.3 darrenr * Id: ip_ipsec_pxy.c,v 1.1.1.2 2012/07/22 13:45:19 darrenr Exp 12 1.1 christos * 13 1.1 christos */ 14 1.2 christos 15 1.2 christos #include <sys/cdefs.h> 16 1.3 darrenr __KERNEL_RCSID(1, "$NetBSD: ip_ipsec_pxy.c,v 1.3 2012/07/22 14:27:51 darrenr Exp $"); 17 1.2 christos 18 1.1 christos #define IPF_IPSEC_PROXY 19 1.1 christos 20 1.1 christos 21 1.3 darrenr /* 22 1.3 darrenr * IPSec proxy 23 1.3 darrenr */ 24 1.3 darrenr typedef struct ipf_ipsec_softc_s { 25 1.3 darrenr frentry_t ipsec_fr; 26 1.3 darrenr int ipsec_proxy_init; 27 1.3 darrenr int ipsec_proxy_ttl; 28 1.3 darrenr ipftq_t *ipsec_nat_tqe; 29 1.3 darrenr ipftq_t *ipsec_state_tqe; 30 1.3 darrenr char ipsec_buffer[1500]; 31 1.3 darrenr } ipf_ipsec_softc_t; 32 1.3 darrenr 33 1.3 darrenr 34 1.2 christos void *ipf_p_ipsec_soft_create(ipf_main_softc_t *); 35 1.2 christos void ipf_p_ipsec_soft_destroy(ipf_main_softc_t *, void *); 36 1.2 christos int ipf_p_ipsec_soft_init(ipf_main_softc_t *, void *); 37 1.2 christos void ipf_p_ipsec_soft_fini(ipf_main_softc_t *, void *); 38 1.2 christos int ipf_p_ipsec_init(void); 39 1.2 christos void ipf_p_ipsec_fini(void); 40 1.2 christos int ipf_p_ipsec_new(void *, fr_info_t *, ap_session_t *, nat_t *); 41 1.2 christos void ipf_p_ipsec_del(ipf_main_softc_t *, ap_session_t *); 42 1.2 christos int ipf_p_ipsec_inout(void *, fr_info_t *, ap_session_t *, nat_t *); 43 1.2 christos int ipf_p_ipsec_match(fr_info_t *, ap_session_t *, nat_t *); 44 1.1 christos 45 1.1 christos 46 1.1 christos /* 47 1.1 christos * IPSec application proxy initialization. 48 1.1 christos */ 49 1.1 christos void * 50 1.2 christos ipf_p_ipsec_soft_create(ipf_main_softc_t *softc) 51 1.1 christos { 52 1.1 christos ipf_ipsec_softc_t *softi; 53 1.1 christos 54 1.1 christos KMALLOC(softi, ipf_ipsec_softc_t *); 55 1.1 christos if (softi == NULL) 56 1.1 christos return NULL; 57 1.1 christos 58 1.1 christos bzero((char *)softi, sizeof(*softi)); 59 1.1 christos softi->ipsec_fr.fr_ref = 1; 60 1.1 christos softi->ipsec_fr.fr_flags = FR_OUTQUE|FR_PASS|FR_QUICK|FR_KEEPSTATE; 61 1.1 christos MUTEX_INIT(&softi->ipsec_fr.fr_lock, "IPsec proxy rule lock"); 62 1.1 christos softi->ipsec_proxy_init = 1; 63 1.1 christos softi->ipsec_proxy_ttl = 60; 64 1.1 christos 65 1.1 christos return softi; 66 1.1 christos } 67 1.1 christos 68 1.1 christos 69 1.1 christos int 70 1.2 christos ipf_p_ipsec_soft_init(ipf_main_softc_t *softc, void *arg) 71 1.1 christos { 72 1.1 christos ipf_ipsec_softc_t *softi = arg; 73 1.1 christos 74 1.1 christos softi->ipsec_nat_tqe = ipf_state_add_tq(softc, softi->ipsec_proxy_ttl); 75 1.1 christos if (softi->ipsec_nat_tqe == NULL) 76 1.1 christos return -1; 77 1.1 christos softi->ipsec_state_tqe = ipf_nat_add_tq(softc, softi->ipsec_proxy_ttl); 78 1.1 christos if (softi->ipsec_state_tqe == NULL) { 79 1.1 christos if (ipf_deletetimeoutqueue(softi->ipsec_nat_tqe) == 0) 80 1.1 christos ipf_freetimeoutqueue(softc, softi->ipsec_nat_tqe); 81 1.1 christos softi->ipsec_nat_tqe = NULL; 82 1.1 christos return -1; 83 1.1 christos } 84 1.1 christos 85 1.1 christos softi->ipsec_nat_tqe->ifq_flags |= IFQF_PROXY; 86 1.1 christos softi->ipsec_state_tqe->ifq_flags |= IFQF_PROXY; 87 1.1 christos softi->ipsec_fr.fr_age[0] = softi->ipsec_proxy_ttl; 88 1.1 christos softi->ipsec_fr.fr_age[1] = softi->ipsec_proxy_ttl; 89 1.1 christos return 0; 90 1.1 christos } 91 1.1 christos 92 1.1 christos 93 1.1 christos void 94 1.2 christos ipf_p_ipsec_soft_fini(ipf_main_softc_t *softc, void *arg) 95 1.1 christos { 96 1.1 christos ipf_ipsec_softc_t *softi = arg; 97 1.1 christos 98 1.1 christos if (arg == NULL) 99 1.1 christos return; 100 1.1 christos 101 1.1 christos if (softi->ipsec_nat_tqe != NULL) { 102 1.1 christos if (ipf_deletetimeoutqueue(softi->ipsec_nat_tqe) == 0) 103 1.1 christos ipf_freetimeoutqueue(softc, softi->ipsec_nat_tqe); 104 1.1 christos } 105 1.1 christos softi->ipsec_nat_tqe = NULL; 106 1.1 christos if (softi->ipsec_state_tqe != NULL) { 107 1.1 christos if (ipf_deletetimeoutqueue(softi->ipsec_state_tqe) == 0) 108 1.1 christos ipf_freetimeoutqueue(softc, softi->ipsec_state_tqe); 109 1.1 christos } 110 1.1 christos softi->ipsec_state_tqe = NULL; 111 1.1 christos } 112 1.1 christos 113 1.1 christos 114 1.1 christos void 115 1.2 christos ipf_p_ipsec_soft_destroy(ipf_main_softc_t *softc, void *arg) 116 1.1 christos { 117 1.1 christos ipf_ipsec_softc_t *softi = arg; 118 1.1 christos 119 1.1 christos if (softi->ipsec_proxy_init == 1) { 120 1.1 christos MUTEX_DESTROY(&softi->ipsec_fr.fr_lock); 121 1.1 christos softi->ipsec_proxy_init = 0; 122 1.1 christos } 123 1.1 christos 124 1.1 christos KFREE(softi); 125 1.1 christos } 126 1.1 christos 127 1.1 christos 128 1.1 christos /* 129 1.1 christos * Setup for a new IPSEC proxy. 130 1.1 christos */ 131 1.1 christos int 132 1.2 christos ipf_p_ipsec_new(void *arg, fr_info_t *fin, ap_session_t *aps, nat_t *nat) 133 1.1 christos { 134 1.1 christos ipf_ipsec_softc_t *softi = arg; 135 1.1 christos ipf_main_softc_t *softc = fin->fin_main_soft; 136 1.1 christos #ifdef USE_MUTEXES 137 1.1 christos ipf_nat_softc_t *softn = softc->ipf_nat_soft; 138 1.1 christos #endif 139 1.3 darrenr int p, off, dlen, ttl; 140 1.1 christos ipsec_pxy_t *ipsec; 141 1.1 christos ipnat_t *ipn, *np; 142 1.1 christos fr_info_t fi; 143 1.1 christos char *ptr; 144 1.3 darrenr int size; 145 1.3 darrenr ip_t *ip; 146 1.1 christos mb_t *m; 147 1.3 darrenr 148 1.3 darrenr if (fin->fin_v != 4) 149 1.3 darrenr return -1; 150 1.1 christos 151 1.1 christos off = fin->fin_plen - fin->fin_dlen + fin->fin_ipoff; 152 1.1 christos bzero(softi->ipsec_buffer, sizeof(softi->ipsec_buffer)); 153 1.1 christos ip = fin->fin_ip; 154 1.1 christos m = fin->fin_m; 155 1.1 christos 156 1.1 christos dlen = M_LEN(m) - off; 157 1.1 christos if (dlen < 16) 158 1.1 christos return -1; 159 1.1 christos COPYDATA(m, off, MIN(sizeof(softi->ipsec_buffer), dlen), 160 1.1 christos softi->ipsec_buffer); 161 1.1 christos 162 1.1 christos if (ipf_nat_outlookup(fin, 0, IPPROTO_ESP, nat->nat_nsrcip, 163 1.1 christos ip->ip_dst) != NULL) 164 1.1 christos return -1; 165 1.1 christos 166 1.1 christos np = nat->nat_ptr; 167 1.3 darrenr size = np->in_size; 168 1.3 darrenr KMALLOC(ipsec, ipsec_pxy_t *); 169 1.3 darrenr if (ipsec == NULL) 170 1.1 christos return -1; 171 1.1 christos 172 1.3 darrenr KMALLOCS(ipn, ipnat_t *, size); 173 1.3 darrenr if (ipn == NULL) { 174 1.3 darrenr KFREE(ipsec); 175 1.3 darrenr return -1; 176 1.3 darrenr } 177 1.3 darrenr 178 1.3 darrenr aps->aps_data = ipsec; 179 1.3 darrenr aps->aps_psiz = sizeof(*ipsec); 180 1.1 christos bzero((char *)ipsec, sizeof(*ipsec)); 181 1.3 darrenr bzero((char *)ipn, size); 182 1.3 darrenr ipsec->ipsc_rule = ipn; 183 1.1 christos 184 1.1 christos /* 185 1.1 christos * Create NAT rule against which the tunnel/transport mapping is 186 1.1 christos * created. This is required because the current NAT rule does not 187 1.1 christos * describe ESP but UDP instead. 188 1.1 christos */ 189 1.3 darrenr ipn->in_size = size; 190 1.1 christos ttl = IPF_TTLVAL(softi->ipsec_nat_tqe->ifq_ttl); 191 1.1 christos ipn->in_tqehead[0] = ipf_nat_add_tq(softc, ttl); 192 1.1 christos ipn->in_tqehead[1] = ipf_nat_add_tq(softc, ttl); 193 1.1 christos ipn->in_ifps[0] = fin->fin_ifp; 194 1.1 christos ipn->in_apr = NULL; 195 1.1 christos ipn->in_use = 1; 196 1.1 christos ipn->in_hits = 1; 197 1.1 christos ipn->in_snip = ntohl(nat->nat_nsrcaddr); 198 1.1 christos ipn->in_ippip = 1; 199 1.1 christos ipn->in_osrcip = nat->nat_osrcip; 200 1.1 christos ipn->in_osrcmsk = 0xffffffff; 201 1.1 christos ipn->in_nsrcip = nat->nat_nsrcip; 202 1.1 christos ipn->in_nsrcmsk = 0xffffffff; 203 1.1 christos ipn->in_odstip = nat->nat_odstip; 204 1.1 christos ipn->in_odstmsk = 0xffffffff; 205 1.1 christos ipn->in_ndstip = nat->nat_ndstip; 206 1.1 christos ipn->in_ndstmsk = 0xffffffff; 207 1.1 christos ipn->in_redir = NAT_MAP; 208 1.1 christos ipn->in_pr[0] = IPPROTO_ESP; 209 1.1 christos ipn->in_pr[1] = IPPROTO_ESP; 210 1.3 darrenr ipn->in_flags = (np->in_flags | IPN_PROXYRULE); 211 1.1 christos MUTEX_INIT(&ipn->in_lock, "IPSec proxy NAT rule"); 212 1.1 christos 213 1.1 christos ipn->in_namelen = np->in_namelen; 214 1.1 christos bcopy(np->in_names, ipn->in_ifnames, ipn->in_namelen); 215 1.1 christos ipn->in_ifnames[0] = np->in_ifnames[0]; 216 1.1 christos ipn->in_ifnames[1] = np->in_ifnames[1]; 217 1.1 christos 218 1.1 christos bcopy((char *)fin, (char *)&fi, sizeof(fi)); 219 1.1 christos fi.fin_fi.fi_p = IPPROTO_ESP; 220 1.1 christos fi.fin_fr = &softi->ipsec_fr; 221 1.1 christos fi.fin_data[0] = 0; 222 1.1 christos fi.fin_data[1] = 0; 223 1.1 christos p = ip->ip_p; 224 1.1 christos ip->ip_p = IPPROTO_ESP; 225 1.1 christos fi.fin_flx &= ~(FI_TCPUDP|FI_STATE|FI_FRAG); 226 1.1 christos fi.fin_flx |= FI_IGNORE; 227 1.1 christos 228 1.1 christos ptr = softi->ipsec_buffer; 229 1.1 christos bcopy(ptr, (char *)ipsec->ipsc_icookie, sizeof(ipsec_cookie_t)); 230 1.1 christos ptr += sizeof(ipsec_cookie_t); 231 1.1 christos bcopy(ptr, (char *)ipsec->ipsc_rcookie, sizeof(ipsec_cookie_t)); 232 1.1 christos /* 233 1.1 christos * The responder cookie should only be non-zero if the initiator 234 1.1 christos * cookie is non-zero. Therefore, it is safe to assume(!) that the 235 1.1 christos * cookies are both set after copying if the responder is non-zero. 236 1.1 christos */ 237 1.1 christos if ((ipsec->ipsc_rcookie[0]|ipsec->ipsc_rcookie[1]) != 0) 238 1.1 christos ipsec->ipsc_rckset = 1; 239 1.1 christos 240 1.1 christos MUTEX_ENTER(&softn->ipf_nat_new); 241 1.1 christos ipsec->ipsc_nat = ipf_nat_add(&fi, ipn, &ipsec->ipsc_nat, 242 1.1 christos NAT_SLAVE|SI_WILDP, NAT_OUTBOUND); 243 1.1 christos MUTEX_EXIT(&softn->ipf_nat_new); 244 1.1 christos if (ipsec->ipsc_nat != NULL) { 245 1.1 christos (void) ipf_nat_proto(&fi, ipsec->ipsc_nat, 0); 246 1.1 christos MUTEX_ENTER(&ipsec->ipsc_nat->nat_lock); 247 1.1 christos ipf_nat_update(&fi, ipsec->ipsc_nat); 248 1.1 christos MUTEX_EXIT(&ipsec->ipsc_nat->nat_lock); 249 1.1 christos 250 1.1 christos fi.fin_data[0] = 0; 251 1.1 christos fi.fin_data[1] = 0; 252 1.1 christos (void) ipf_state_add(softc, &fi, &ipsec->ipsc_state, SI_WILDP); 253 1.1 christos } 254 1.1 christos ip->ip_p = p & 0xff; 255 1.1 christos return 0; 256 1.1 christos } 257 1.1 christos 258 1.1 christos 259 1.1 christos /* 260 1.1 christos * For outgoing IKE packets. refresh timeouts for NAT & state entries, if 261 1.1 christos * we can. If they have disappeared, recreate them. 262 1.1 christos */ 263 1.1 christos int 264 1.2 christos ipf_p_ipsec_inout(void *arg, fr_info_t *fin, ap_session_t *aps, nat_t *nat) 265 1.1 christos { 266 1.1 christos ipf_ipsec_softc_t *softi = arg; 267 1.1 christos ipf_main_softc_t *softc = fin->fin_main_soft; 268 1.1 christos ipsec_pxy_t *ipsec; 269 1.1 christos fr_info_t fi; 270 1.1 christos ip_t *ip; 271 1.1 christos int p; 272 1.1 christos 273 1.1 christos if ((fin->fin_out == 1) && (nat->nat_dir == NAT_INBOUND)) 274 1.1 christos return 0; 275 1.1 christos 276 1.1 christos if ((fin->fin_out == 0) && (nat->nat_dir == NAT_OUTBOUND)) 277 1.1 christos return 0; 278 1.1 christos 279 1.1 christos ipsec = aps->aps_data; 280 1.1 christos 281 1.1 christos if (ipsec != NULL) { 282 1.1 christos ip = fin->fin_ip; 283 1.1 christos p = ip->ip_p; 284 1.1 christos 285 1.1 christos if ((ipsec->ipsc_nat == NULL) || (ipsec->ipsc_state == NULL)) { 286 1.1 christos bcopy((char *)fin, (char *)&fi, sizeof(fi)); 287 1.1 christos fi.fin_fi.fi_p = IPPROTO_ESP; 288 1.1 christos fi.fin_fr = &softi->ipsec_fr; 289 1.1 christos fi.fin_data[0] = 0; 290 1.1 christos fi.fin_data[1] = 0; 291 1.1 christos ip->ip_p = IPPROTO_ESP; 292 1.1 christos fi.fin_flx &= ~(FI_TCPUDP|FI_STATE|FI_FRAG); 293 1.1 christos fi.fin_flx |= FI_IGNORE; 294 1.1 christos } 295 1.1 christos 296 1.1 christos /* 297 1.1 christos * Update NAT timeout/create NAT if missing. 298 1.1 christos */ 299 1.1 christos if (ipsec->ipsc_nat != NULL) 300 1.1 christos ipf_queueback(softc->ipf_ticks, 301 1.1 christos &ipsec->ipsc_nat->nat_tqe); 302 1.1 christos else { 303 1.1 christos #ifdef USE_MUTEXES 304 1.1 christos ipf_nat_softc_t *softn = softc->ipf_nat_soft; 305 1.1 christos #endif 306 1.1 christos 307 1.1 christos MUTEX_ENTER(&softn->ipf_nat_new); 308 1.3 darrenr ipsec->ipsc_nat = ipf_nat_add(&fi, ipsec->ipsc_rule, 309 1.1 christos &ipsec->ipsc_nat, 310 1.1 christos NAT_SLAVE|SI_WILDP, 311 1.1 christos nat->nat_dir); 312 1.1 christos MUTEX_EXIT(&softn->ipf_nat_new); 313 1.1 christos if (ipsec->ipsc_nat != NULL) { 314 1.1 christos (void) ipf_nat_proto(&fi, ipsec->ipsc_nat, 0); 315 1.1 christos MUTEX_ENTER(&ipsec->ipsc_nat->nat_lock); 316 1.1 christos ipf_nat_update(&fi, ipsec->ipsc_nat); 317 1.1 christos MUTEX_EXIT(&ipsec->ipsc_nat->nat_lock); 318 1.1 christos } 319 1.1 christos } 320 1.1 christos 321 1.1 christos /* 322 1.1 christos * Update state timeout/create state if missing. 323 1.1 christos */ 324 1.1 christos READ_ENTER(&softc->ipf_state); 325 1.1 christos if (ipsec->ipsc_state != NULL) { 326 1.1 christos ipf_queueback(softc->ipf_ticks, 327 1.1 christos &ipsec->ipsc_state->is_sti); 328 1.1 christos ipsec->ipsc_state->is_die = nat->nat_age; 329 1.1 christos RWLOCK_EXIT(&softc->ipf_state); 330 1.1 christos } else { 331 1.1 christos RWLOCK_EXIT(&softc->ipf_state); 332 1.1 christos fi.fin_data[0] = 0; 333 1.1 christos fi.fin_data[1] = 0; 334 1.1 christos (void) ipf_state_add(softc, &fi, &ipsec->ipsc_state, 335 1.1 christos SI_WILDP); 336 1.1 christos } 337 1.1 christos ip->ip_p = p; 338 1.1 christos } 339 1.1 christos return 0; 340 1.1 christos } 341 1.1 christos 342 1.1 christos 343 1.1 christos /* 344 1.1 christos * This extends the NAT matching to be based on the cookies associated with 345 1.1 christos * a session and found at the front of IKE packets. The cookies are always 346 1.1 christos * in the same order (not reversed depending on packet flow direction as with 347 1.1 christos * UDP/TCP port numbers). 348 1.1 christos */ 349 1.1 christos int 350 1.2 christos ipf_p_ipsec_match(fr_info_t *fin, ap_session_t *aps, nat_t *nat) 351 1.1 christos { 352 1.1 christos ipsec_pxy_t *ipsec; 353 1.1 christos u_32_t cookies[4]; 354 1.1 christos mb_t *m; 355 1.1 christos int off; 356 1.1 christos 357 1.1 christos nat = nat; /* LINT */ 358 1.1 christos 359 1.1 christos if ((fin->fin_dlen < sizeof(cookies)) || (fin->fin_flx & FI_FRAG)) 360 1.1 christos return -1; 361 1.1 christos 362 1.1 christos off = fin->fin_plen - fin->fin_dlen + fin->fin_ipoff; 363 1.1 christos ipsec = aps->aps_data; 364 1.1 christos m = fin->fin_m; 365 1.1 christos COPYDATA(m, off, sizeof(cookies), (char *)cookies); 366 1.1 christos 367 1.1 christos if ((cookies[0] != ipsec->ipsc_icookie[0]) || 368 1.1 christos (cookies[1] != ipsec->ipsc_icookie[1])) 369 1.1 christos return -1; 370 1.1 christos 371 1.1 christos if (ipsec->ipsc_rckset == 0) { 372 1.1 christos if ((cookies[2]|cookies[3]) == 0) { 373 1.1 christos return 0; 374 1.1 christos } 375 1.1 christos ipsec->ipsc_rckset = 1; 376 1.1 christos ipsec->ipsc_rcookie[0] = cookies[2]; 377 1.1 christos ipsec->ipsc_rcookie[1] = cookies[3]; 378 1.1 christos return 0; 379 1.1 christos } 380 1.1 christos 381 1.1 christos if ((cookies[2] != ipsec->ipsc_rcookie[0]) || 382 1.1 christos (cookies[3] != ipsec->ipsc_rcookie[1])) 383 1.1 christos return -1; 384 1.1 christos return 0; 385 1.1 christos } 386 1.1 christos 387 1.1 christos 388 1.1 christos /* 389 1.1 christos * clean up after ourselves. 390 1.1 christos */ 391 1.1 christos void 392 1.2 christos ipf_p_ipsec_del(ipf_main_softc_t *softc, ap_session_t *aps) 393 1.1 christos { 394 1.1 christos ipsec_pxy_t *ipsec; 395 1.1 christos 396 1.1 christos ipsec = aps->aps_data; 397 1.1 christos 398 1.1 christos if (ipsec != NULL) { 399 1.1 christos /* 400 1.1 christos * Don't bother changing any of the NAT structure details, 401 1.1 christos * *_del() is on a callback from aps_free(), from nat_delete() 402 1.1 christos */ 403 1.1 christos 404 1.1 christos READ_ENTER(&softc->ipf_state); 405 1.1 christos if (ipsec->ipsc_state != NULL) { 406 1.1 christos ipsec->ipsc_state->is_die = softc->ipf_ticks + 1; 407 1.1 christos ipsec->ipsc_state->is_me = NULL; 408 1.1 christos ipf_queuefront(&ipsec->ipsc_state->is_sti); 409 1.1 christos } 410 1.1 christos RWLOCK_EXIT(&softc->ipf_state); 411 1.1 christos 412 1.1 christos ipsec->ipsc_state = NULL; 413 1.1 christos ipsec->ipsc_nat = NULL; 414 1.3 darrenr ipsec->ipsc_rule->in_flags |= IPN_DELETE; 415 1.3 darrenr ipf_nat_rule_deref(softc, &ipsec->ipsc_rule); 416 1.1 christos } 417 1.1 christos } 418