ip_auth.c revision 55929
162583Sitojun/* 278064Sume * Copyright (C) 1998 by Darren Reed & Guido van Rooij. 362583Sitojun * 455505Sshin * Redistribution and use in source and binary forms are permitted 555505Sshin * provided that this notice is preserved and due credit is given 655505Sshin * to the original author and the contributors. 755505Sshin */ 855505Sshin#if !defined(lint) 955505Sshin/*static const char rcsid[] = "@(#)$Id: ip_auth.c,v 2.1.2.1 1999/09/28 11:44:04 darrenr Exp $";*/ 1055505Sshinstatic const char rcsid[] = "@(#)$FreeBSD: head/sys/contrib/ipfilter/netinet/ip_auth.c 55929 2000-01-13 19:01:33Z guido $"; 1155505Sshin#endif 1255505Sshin 1355505Sshin#include <sys/errno.h> 1455505Sshin#include <sys/types.h> 1555505Sshin#include <sys/param.h> 1655505Sshin#include <sys/time.h> 1755505Sshin#include <sys/file.h> 1855505Sshin#if !defined(_KERNEL) && !defined(KERNEL) 1955505Sshin# include <stdio.h> 2055505Sshin# include <stdlib.h> 2155505Sshin# include <string.h> 2255505Sshin#endif 2355505Sshin#if ((defined(KERNEL) && (__FreeBSD_version >= 220000)) || \ 2455505Sshin (defined(_KERNEL) && (__FreeBSD_version >= 40013))) 2555505Sshin# include <sys/filio.h> 2655505Sshin# include <sys/fcntl.h> 2755505Sshin#else 2855505Sshin# include <sys/ioctl.h> 2955505Sshin#endif 3055505Sshin#include <sys/uio.h> 3155505Sshin#ifndef linux 3255505Sshin# include <sys/protosw.h> 3362583Sitojun#endif 3492917Sobrien#include <sys/socket.h> 3555505Sshin#if defined(_KERNEL) && !defined(linux) 3662583Sitojun# include <sys/systm.h> 3762583Sitojun#endif 3862583Sitojun#if !defined(__SVR4) && !defined(__svr4__) 3962583Sitojun# ifndef linux 4062583Sitojun# include <sys/mbuf.h> 4162583Sitojun# endif 4262583Sitojun#else 4362583Sitojun# include <sys/filio.h> 4462583Sitojun# include <sys/byteorder.h> 4562583Sitojun# ifdef _KERNEL 4662583Sitojun# include <sys/dditypes.h> 4762583Sitojun# endif 4862583Sitojun# include <sys/stream.h> 4962583Sitojun# include <sys/kmem.h> 5062583Sitojun#endif 5162583Sitojun#if _BSDI_VERSION >= 199802 5262583Sitojun# include <sys/queue.h> 5362583Sitojun#endif 5462583Sitojun#if defined(__NetBSD__) || defined(__OpenBSD__) || defined(bsdi) 5562583Sitojun# include <machine/cpu.h> 5662583Sitojun#endif 5762583Sitojun#include <net/if.h> 5862583Sitojun#ifdef sun 5962583Sitojun# include <net/af.h> 6062583Sitojun#endif 6162583Sitojun#include <net/route.h> 6262583Sitojun#include <netinet/in.h> 6362583Sitojun#include <netinet/in_systm.h> 64#include <netinet/ip.h> 65#ifndef KERNEL 66# define KERNEL 67# define NOT_KERNEL 68#endif 69#ifndef linux 70# include <netinet/ip_var.h> 71#endif 72#ifdef NOT_KERNEL 73# undef KERNEL 74#endif 75#ifdef __sgi 76# ifdef IFF_DRVRLOCK /* IRIX6 */ 77# include <sys/hashing.h> 78# endif 79#endif 80#include <netinet/tcp.h> 81#if defined(__sgi) && !defined(IFF_DRVRLOCK) /* IRIX < 6 */ 82extern struct ifqueue ipintrq; /* ip packet input queue */ 83#else 84# ifndef linux 85# if __FreeBSD_version >= 300000 86# include <net/if_var.h> 87# endif 88# include <netinet/in_var.h> 89# include <netinet/tcp_fsm.h> 90# endif 91#endif 92#include <netinet/udp.h> 93#include <netinet/ip_icmp.h> 94#include "netinet/ip_compat.h" 95#include <netinet/tcpip.h> 96#include "netinet/ip_fil.h" 97#include "netinet/ip_auth.h" 98#if !SOLARIS && !defined(linux) 99# include <net/netisr.h> 100# ifdef __FreeBSD__ 101# include <machine/cpufunc.h> 102# endif 103#endif 104#if (__FreeBSD_version >= 300000) 105# include <sys/malloc.h> 106# if (defined(_KERNEL) || defined(KERNEL)) && !defined(IPFILTER_LKM) 107# include <sys/libkern.h> 108# include <sys/systm.h> 109# endif 110#endif 111 112 113 114#if (SOLARIS || defined(__sgi)) && defined(_KERNEL) 115extern KRWLOCK_T ipf_auth; 116extern kmutex_t ipf_authmx; 117# if SOLARIS 118extern kcondvar_t ipfauthwait; 119# endif 120#endif 121#ifdef linux 122static struct wait_queue *ipfauthwait = NULL; 123#endif 124 125int fr_authsize = FR_NUMAUTH; 126int fr_authused = 0; 127int fr_defaultauthage = 600; 128fr_authstat_t fr_authstats; 129frauth_t fr_auth[FR_NUMAUTH]; 130mb_t *fr_authpkts[FR_NUMAUTH]; 131int fr_authstart = 0, fr_authend = 0, fr_authnext = 0; 132frauthent_t *fae_list = NULL; 133frentry_t *ipauth = NULL; 134 135 136/* 137 * Check if a packet has authorization. If the packet is found to match an 138 * authorization result and that would result in a feedback loop (i.e. it 139 * will end up returning FR_AUTH) then return FR_BLOCK instead. 140 */ 141u_32_t fr_checkauth(ip, fin) 142ip_t *ip; 143fr_info_t *fin; 144{ 145 u_short id = ip->ip_id; 146 u_32_t pass; 147 int i; 148 149 READ_ENTER(&ipf_auth); 150 for (i = fr_authstart; i != fr_authend; ) { 151 /* 152 * index becomes -2 only after an SIOCAUTHW. Check this in 153 * case the same packet gets sent again and it hasn't yet been 154 * auth'd. 155 */ 156 if ((fr_auth[i].fra_index == -2) && 157 (id == fr_auth[i].fra_info.fin_id) && 158 !bcmp((char *)fin,(char *)&fr_auth[i].fra_info,FI_CSIZE)) { 159 /* 160 * Avoid feedback loop. 161 */ 162 if (!(pass = fr_auth[i].fra_pass) || (pass & FR_AUTH)) 163 pass = FR_BLOCK; 164 RWLOCK_EXIT(&ipf_auth); 165 WRITE_ENTER(&ipf_auth); 166 fr_authstats.fas_hits++; 167 fr_auth[i].fra_index = -1; 168 fr_authused--; 169 if (i == fr_authstart) { 170 while (fr_auth[i].fra_index == -1) { 171 i++; 172 if (i == FR_NUMAUTH) 173 i = 0; 174 fr_authstart = i; 175 if (i == fr_authend) 176 break; 177 } 178 if (fr_authstart == fr_authend) { 179 fr_authnext = 0; 180 fr_authstart = fr_authend = 0; 181 } 182 } 183 RWLOCK_EXIT(&ipf_auth); 184 return pass; 185 } 186 i++; 187 if (i == FR_NUMAUTH) 188 i = 0; 189 } 190 fr_authstats.fas_miss++; 191 RWLOCK_EXIT(&ipf_auth); 192 return 0; 193} 194 195 196/* 197 * Check if we have room in the auth array to hold details for another packet. 198 * If we do, store it and wake up any user programs which are waiting to 199 * hear about these events. 200 */ 201int fr_newauth(m, fin, ip 202#if defined(_KERNEL) && SOLARIS 203, qif) 204qif_t *qif; 205#else 206) 207#endif 208mb_t *m; 209fr_info_t *fin; 210ip_t *ip; 211{ 212 int i; 213 214 WRITE_ENTER(&ipf_auth); 215 if (fr_authstart > fr_authend) { 216 fr_authstats.fas_nospace++; 217 RWLOCK_EXIT(&ipf_auth); 218 return 0; 219 } else { 220 if ((fr_authstart == 0) && (fr_authend == FR_NUMAUTH - 1)) { 221 fr_authstats.fas_nospace++; 222 RWLOCK_EXIT(&ipf_auth); 223 return 0; 224 } 225 } 226 227 fr_authstats.fas_added++; 228 fr_authused++; 229 i = fr_authend++; 230 if (fr_authend == FR_NUMAUTH) 231 fr_authend = 0; 232 RWLOCK_EXIT(&ipf_auth); 233 fr_auth[i].fra_index = i; 234 fr_auth[i].fra_pass = 0; 235 fr_auth[i].fra_age = fr_defaultauthage; 236 bcopy((char *)fin, (char *)&fr_auth[i].fra_info, sizeof(*fin)); 237#if !defined(sparc) && !defined(m68k) 238 /* 239 * No need to copyback here as we want to undo the changes, not keep 240 * them. 241 */ 242# if SOLARIS && defined(_KERNEL) 243 if (ip == (ip_t *)m->b_rptr) 244# endif 245 { 246 register u_short bo; 247 248 bo = ip->ip_len; 249 ip->ip_len = htons(bo); 250# if !SOLARIS /* 4.4BSD converts this ip_input.c, but I don't in solaris.c */ 251 bo = ip->ip_id; 252 ip->ip_id = htons(bo); 253# endif 254 bo = ip->ip_off; 255 ip->ip_off = htons(bo); 256 } 257#endif 258#if SOLARIS && defined(_KERNEL) 259 m->b_rptr -= qif->qf_off; 260 fr_authpkts[i] = *(mblk_t **)fin->fin_mp; 261 fr_auth[i].fra_q = qif->qf_q; 262 cv_signal(&ipfauthwait); 263#else 264 fr_authpkts[i] = m; 265# if defined(linux) && defined(_KERNEL) 266 wake_up_interruptible(&ipfauthwait); 267# else 268 WAKEUP(&fr_authnext); 269# endif 270#endif 271 return 1; 272} 273 274 275int fr_auth_ioctl(data, cmd, fr, frptr) 276caddr_t data; 277#if defined(__NetBSD__) || defined(__OpenBSD__) 278u_long cmd; 279#else 280int cmd; 281#endif 282frentry_t *fr, **frptr; 283{ 284 mb_t *m; 285#if defined(_KERNEL) 286# if !SOLARIS 287 struct ifqueue *ifq; 288 int s; 289# endif 290#endif 291 frauth_t auth, *au = &auth; 292 frauthent_t *fae, **faep; 293 int i, error = 0; 294 295 switch (cmd) 296 { 297 case SIOCINIFR : 298 case SIOCRMIFR : 299 case SIOCADIFR : 300 error = EINVAL; 301 break; 302 case SIOCINAFR : 303 case SIOCRMAFR : 304 case SIOCADAFR : 305 for (faep = &fae_list; (fae = *faep); ) 306 if (&fae->fae_fr == fr) 307 break; 308 else 309 faep = &fae->fae_next; 310 if (cmd == SIOCRMAFR) { 311 if (!fae) 312 error = ESRCH; 313 else { 314 WRITE_ENTER(&ipf_auth); 315 *faep = fae->fae_next; 316 *frptr = fr->fr_next; 317 RWLOCK_EXIT(&ipf_auth); 318 KFREE(fae); 319 } 320 } else { 321 KMALLOC(fae, frauthent_t *); 322 if (fae != NULL) { 323 IRCOPY((char *)data, (char *)&fae->fae_fr, 324 sizeof(fae->fae_fr)); 325 WRITE_ENTER(&ipf_auth); 326 fae->fae_age = fr_defaultauthage; 327 fae->fae_fr.fr_hits = 0; 328 fae->fae_fr.fr_next = *frptr; 329 *frptr = &fae->fae_fr; 330 fae->fae_next = *faep; 331 *faep = fae; 332 ipauth = &fae_list->fae_fr; 333 RWLOCK_EXIT(&ipf_auth); 334 } else 335 error = ENOMEM; 336 } 337 break; 338 case SIOCATHST: 339 READ_ENTER(&ipf_auth); 340 fr_authstats.fas_faelist = fae_list; 341 RWLOCK_EXIT(&ipf_auth); 342 IWCOPY((char *)&fr_authstats, data, sizeof(fr_authstats)); 343 break; 344 case SIOCAUTHW: 345fr_authioctlloop: 346 READ_ENTER(&ipf_auth); 347 if ((fr_authnext != fr_authend) && fr_authpkts[fr_authnext]) { 348 IWCOPY((char *)&fr_auth[fr_authnext], data, 349 sizeof(fr_info_t)); 350 RWLOCK_EXIT(&ipf_auth); 351 WRITE_ENTER(&ipf_auth); 352 fr_authnext++; 353 if (fr_authnext == FR_NUMAUTH) 354 fr_authnext = 0; 355 RWLOCK_EXIT(&ipf_auth); 356 return 0; 357 } 358#ifdef _KERNEL 359# if SOLARIS 360 mutex_enter(&ipf_authmx); 361 if (!cv_wait_sig(&ipfauthwait, &ipf_authmx)) { 362 mutex_exit(&ipf_authmx); 363 return EINTR; 364 } 365 mutex_exit(&ipf_authmx); 366# else 367# ifdef linux 368 interruptible_sleep_on(&ipfauthwait); 369 if (current->signal & ~current->blocked) 370 error = -EINTR; 371# else 372 error = SLEEP(&fr_authnext, "fr_authnext"); 373# endif 374# endif 375#endif 376 RWLOCK_EXIT(&ipf_auth); 377 if (!error) 378 goto fr_authioctlloop; 379 break; 380 case SIOCAUTHR: 381 IRCOPY(data, (caddr_t)&auth, sizeof(auth)); 382 WRITE_ENTER(&ipf_auth); 383 i = au->fra_index; 384 if ((i < 0) || (i > FR_NUMAUTH) || 385 (fr_auth[i].fra_info.fin_id != au->fra_info.fin_id)) { 386 RWLOCK_EXIT(&ipf_auth); 387 return EINVAL; 388 } 389 m = fr_authpkts[i]; 390 fr_auth[i].fra_index = -2; 391 fr_auth[i].fra_pass = au->fra_pass; 392 fr_authpkts[i] = NULL; 393#ifdef _KERNEL 394 RWLOCK_EXIT(&ipf_auth); 395 SPL_NET(s); 396# ifndef linux 397 if (m && au->fra_info.fin_out) { 398# if SOLARIS 399 error = fr_qout(fr_auth[i].fra_q, m); 400# else /* SOLARIS */ 401# if _BSDI_VERSION >= 199802 402 error = ip_output(m, NULL, NULL, IP_FORWARDING, NULL, 403 NULL); 404# else 405 error = ip_output(m, NULL, NULL, IP_FORWARDING, NULL); 406# endif 407# endif /* SOLARIS */ 408 if (error) 409 fr_authstats.fas_sendfail++; 410 else 411 fr_authstats.fas_sendok++; 412 } else if (m) { 413# if SOLARIS 414 error = fr_qin(fr_auth[i].fra_q, m); 415# else /* SOLARIS */ 416 ifq = &ipintrq; 417 if (IF_QFULL(ifq)) { 418 IF_DROP(ifq); 419 m_freem(m); 420 error = ENOBUFS; 421 } else { 422 IF_ENQUEUE(ifq, m); 423 schednetisr(NETISR_IP); 424 } 425# endif /* SOLARIS */ 426 if (error) 427 fr_authstats.fas_quefail++; 428 else 429 fr_authstats.fas_queok++; 430 } else 431 error = EINVAL; 432# endif 433# if SOLARIS 434 if (error) 435 error = EINVAL; 436# else 437 /* 438 * If we experience an error which will result in the packet 439 * not being processed, make sure we advance to the next one. 440 */ 441 if (error == ENOBUFS) { 442 fr_authused--; 443 fr_auth[i].fra_index = -1; 444 fr_auth[i].fra_pass = 0; 445 if (i == fr_authstart) { 446 while (fr_auth[i].fra_index == -1) { 447 i++; 448 if (i == FR_NUMAUTH) 449 i = 0; 450 fr_authstart = i; 451 if (i == fr_authend) 452 break; 453 } 454 if (fr_authstart == fr_authend) { 455 fr_authnext = 0; 456 fr_authstart = fr_authend = 0; 457 } 458 } 459 } 460# endif 461 SPL_X(s); 462#endif /* _KERNEL */ 463 break; 464 default : 465 error = EINVAL; 466 break; 467 } 468 return error; 469} 470 471 472#ifdef _KERNEL 473/* 474 * Free all network buffer memory used to keep saved packets. 475 */ 476void fr_authunload() 477{ 478 register int i; 479 register frauthent_t *fae, **faep; 480 mb_t *m; 481 482 WRITE_ENTER(&ipf_auth); 483 for (i = 0; i < FR_NUMAUTH; i++) { 484 if ((m = fr_authpkts[i])) { 485 FREE_MB_T(m); 486 fr_authpkts[i] = NULL; 487 fr_auth[i].fra_index = -1; 488 } 489 } 490 491 492 for (faep = &fae_list; (fae = *faep); ) { 493 *faep = fae->fae_next; 494 KFREE(fae); 495 } 496 ipauth = NULL; 497 RWLOCK_EXIT(&ipf_auth); 498} 499 500 501/* 502 * Slowly expire held auth records. Timeouts are set 503 * in expectation of this being called twice per second. 504 */ 505void fr_authexpire() 506{ 507 register int i; 508 register frauth_t *fra; 509 register frauthent_t *fae, **faep; 510 mb_t *m; 511#if !SOLARIS 512 int s; 513#endif 514 515 SPL_NET(s); 516 WRITE_ENTER(&ipf_auth); 517 for (i = 0, fra = fr_auth; i < FR_NUMAUTH; i++, fra++) { 518 if ((!--fra->fra_age) && (m = fr_authpkts[i])) { 519 FREE_MB_T(m); 520 fr_authpkts[i] = NULL; 521 fr_auth[i].fra_index = -1; 522 fr_authstats.fas_expire++; 523 fr_authused--; 524 } 525 } 526 527 for (faep = &fae_list; (fae = *faep); ) { 528 if (!--fae->fae_age) { 529 *faep = fae->fae_next; 530 KFREE(fae); 531 fr_authstats.fas_expire++; 532 } else 533 faep = &fae->fae_next; 534 } 535 ipauth = &fae_list->fae_fr; 536 RWLOCK_EXIT(&ipf_auth); 537 SPL_X(s); 538} 539#endif 540