1 /* 2 * Copyright (C) 1993-2001, 2003 by Darren Reed. 3 * 4 * See the IPFILTER.LICENCE file for details on licencing. 5 * 6 * Copyright 2006 Sun Microsystems, Inc. All rights reserved. 7 * Use is subject to license terms. 8 */ 9 /* #pragma ident "@(#)solaris.c 1.12 6/5/96 (C) 1995 Darren Reed"*/ 10 #pragma ident "@(#)$Id: solaris.c,v 2.73.2.6 2005/07/13 21:40:47 darrenr Exp $" 11 12 #pragma ident "%Z%%M% %I% %E% SMI" 13 14 #include <sys/systm.h> 15 #include <sys/types.h> 16 #include <sys/param.h> 17 #include <sys/errno.h> 18 #include <sys/uio.h> 19 #include <sys/buf.h> 20 #include <sys/modctl.h> 21 #include <sys/open.h> 22 #include <sys/kmem.h> 23 #include <sys/conf.h> 24 #include <sys/cmn_err.h> 25 #include <sys/stat.h> 26 #include <sys/cred.h> 27 #include <sys/dditypes.h> 28 #include <sys/poll.h> 29 #include <sys/autoconf.h> 30 #include <sys/byteorder.h> 31 #include <sys/socket.h> 32 #include <sys/dlpi.h> 33 #include <sys/stropts.h> 34 #include <sys/kstat.h> 35 #include <sys/sockio.h> 36 #include <sys/neti.h> 37 #include <sys/hook.h> 38 #include <net/if.h> 39 #if SOLARIS2 >= 6 40 # include <net/if_types.h> 41 #endif 42 #include <net/af.h> 43 #include <net/route.h> 44 #include <netinet/in.h> 45 #include <netinet/in_systm.h> 46 #include <netinet/if_ether.h> 47 #include <netinet/ip.h> 48 #include <netinet/ip_var.h> 49 #include <netinet/tcp.h> 50 #include <netinet/udp.h> 51 #include <netinet/tcpip.h> 52 #include <netinet/ip_icmp.h> 53 #include <sys/ddi.h> 54 #include <sys/sunddi.h> 55 #include "netinet/ip_compat.h" 56 #include "netinet/ipl.h" 57 #include "netinet/ip_fil.h" 58 #include "netinet/ip_nat.h" 59 #include "netinet/ip_frag.h" 60 #include "netinet/ip_auth.h" 61 #include "netinet/ip_state.h" 62 63 extern struct filterstats frstats[]; 64 extern int fr_running; 65 extern int fr_flags; 66 extern int iplwrite __P((dev_t, struct uio *, cred_t *)); 67 68 extern ipnat_t *nat_list; 69 70 static int ipf_getinfo __P((dev_info_t *, ddi_info_cmd_t, 71 void *, void **)); 72 #if SOLARIS2 < 10 73 static int ipf_identify __P((dev_info_t *)); 74 #endif 75 static int ipf_attach __P((dev_info_t *, ddi_attach_cmd_t)); 76 static int ipf_detach __P((dev_info_t *, ddi_detach_cmd_t)); 77 static int ipf_property_update __P((dev_info_t *)); 78 static char *ipf_devfiles[] = { IPL_NAME, IPNAT_NAME, IPSTATE_NAME, 79 IPAUTH_NAME, IPSYNC_NAME, IPSCAN_NAME, 80 IPLOOKUP_NAME, NULL }; 81 82 83 #if SOLARIS2 >= 7 84 extern timeout_id_t fr_timer_id; 85 #else 86 extern int fr_timer_id; 87 #endif 88 89 static struct cb_ops ipf_cb_ops = { 90 iplopen, 91 iplclose, 92 nodev, /* strategy */ 93 nodev, /* print */ 94 nodev, /* dump */ 95 iplread, 96 iplwrite, /* write */ 97 iplioctl, /* ioctl */ 98 nodev, /* devmap */ 99 nodev, /* mmap */ 100 nodev, /* segmap */ 101 nochpoll, /* poll */ 102 ddi_prop_op, 103 NULL, 104 D_MTSAFE, 105 #if SOLARIS2 > 4 106 CB_REV, 107 nodev, /* aread */ 108 nodev, /* awrite */ 109 #endif 110 }; 111 112 static struct dev_ops ipf_ops = { 113 DEVO_REV, 114 0, 115 ipf_getinfo, 116 #if SOLARIS2 >= 10 117 nulldev, 118 #else 119 ipf_identify, 120 #endif 121 nulldev, 122 ipf_attach, 123 ipf_detach, 124 nodev, /* reset */ 125 &ipf_cb_ops, 126 (struct bus_ops *)0 127 }; 128 129 extern struct mod_ops mod_driverops; 130 static struct modldrv iplmod = { 131 &mod_driverops, IPL_VERSION, &ipf_ops }; 132 static struct modlinkage modlink1 = { MODREV_1, &iplmod, NULL }; 133 134 #if SOLARIS2 >= 6 135 static size_t hdrsizes[57][2] = { 136 { 0, 0 }, 137 { IFT_OTHER, 0 }, 138 { IFT_1822, 0 }, 139 { IFT_HDH1822, 0 }, 140 { IFT_X25DDN, 0 }, 141 { IFT_X25, 0 }, 142 { IFT_ETHER, 14 }, 143 { IFT_ISO88023, 0 }, 144 { IFT_ISO88024, 0 }, 145 { IFT_ISO88025, 0 }, 146 { IFT_ISO88026, 0 }, 147 { IFT_STARLAN, 0 }, 148 { IFT_P10, 0 }, 149 { IFT_P80, 0 }, 150 { IFT_HY, 0 }, 151 { IFT_FDDI, 24 }, 152 { IFT_LAPB, 0 }, 153 { IFT_SDLC, 0 }, 154 { IFT_T1, 0 }, 155 { IFT_CEPT, 0 }, 156 { IFT_ISDNBASIC, 0 }, 157 { IFT_ISDNPRIMARY, 0 }, 158 { IFT_PTPSERIAL, 0 }, 159 { IFT_PPP, 0 }, 160 { IFT_LOOP, 0 }, 161 { IFT_EON, 0 }, 162 { IFT_XETHER, 0 }, 163 { IFT_NSIP, 0 }, 164 { IFT_SLIP, 0 }, 165 { IFT_ULTRA, 0 }, 166 { IFT_DS3, 0 }, 167 { IFT_SIP, 0 }, 168 { IFT_FRELAY, 0 }, 169 { IFT_RS232, 0 }, 170 { IFT_PARA, 0 }, 171 { IFT_ARCNET, 0 }, 172 { IFT_ARCNETPLUS, 0 }, 173 { IFT_ATM, 0 }, 174 { IFT_MIOX25, 0 }, 175 { IFT_SONET, 0 }, 176 { IFT_X25PLE, 0 }, 177 { IFT_ISO88022LLC, 0 }, 178 { IFT_LOCALTALK, 0 }, 179 { IFT_SMDSDXI, 0 }, 180 { IFT_FRELAYDCE, 0 }, 181 { IFT_V35, 0 }, 182 { IFT_HSSI, 0 }, 183 { IFT_HIPPI, 0 }, 184 { IFT_MODEM, 0 }, 185 { IFT_AAL5, 0 }, 186 { IFT_SONETPATH, 0 }, 187 { IFT_SONETVT, 0 }, 188 { IFT_SMDSICIP, 0 }, 189 { IFT_PROPVIRTUAL, 0 }, 190 { IFT_PROPMUX, 0 }, 191 }; 192 #endif /* SOLARIS2 >= 6 */ 193 194 static dev_info_t *ipf_dev_info = NULL; 195 196 static const filter_kstats_t ipf_kstat_tmp = { 197 { "pass", KSTAT_DATA_ULONG }, 198 { "block", KSTAT_DATA_ULONG }, 199 { "nomatch", KSTAT_DATA_ULONG }, 200 { "short", KSTAT_DATA_ULONG }, 201 { "pass, logged", KSTAT_DATA_ULONG }, 202 { "block, logged", KSTAT_DATA_ULONG }, 203 { "nomatch, logged", KSTAT_DATA_ULONG }, 204 { "logged", KSTAT_DATA_ULONG }, 205 { "skip", KSTAT_DATA_ULONG }, 206 { "return sent", KSTAT_DATA_ULONG }, 207 { "acct", KSTAT_DATA_ULONG }, 208 { "bad frag state alloc", KSTAT_DATA_ULONG }, 209 { "new frag state kept", KSTAT_DATA_ULONG }, 210 { "new frag state compl. pkt", KSTAT_DATA_ULONG }, 211 { "bad pkt state alloc", KSTAT_DATA_ULONG }, 212 { "new pkt kept state", KSTAT_DATA_ULONG }, 213 { "cachehit", KSTAT_DATA_ULONG }, 214 { "tcp cksum bad", KSTAT_DATA_ULONG }, 215 {{ "pullup ok", KSTAT_DATA_ULONG }, 216 { "pullup nok", KSTAT_DATA_ULONG }}, 217 { "src != route", KSTAT_DATA_ULONG }, 218 { "ttl invalid", KSTAT_DATA_ULONG }, 219 { "bad ip pkt", KSTAT_DATA_ULONG }, 220 { "ipv6 pkt", KSTAT_DATA_ULONG }, 221 { "dropped:pps ceiling", KSTAT_DATA_ULONG }, 222 { "ip upd. fail", KSTAT_DATA_ULONG } 223 }; 224 225 net_data_t ipf_ipv4; 226 net_data_t ipf_ipv6; 227 228 kstat_t *ipf_kstatp[2] = {NULL, NULL}; 229 static int ipf_kstat_update(kstat_t *ksp, int rwflag); 230 231 static void 232 ipf_kstat_init(void) 233 { 234 int i; 235 236 for (i = 0; i < 2; i++) { 237 ipf_kstatp[i] = kstat_create("ipf", 0, 238 (i==0)?"inbound":"outbound", 239 "net", 240 KSTAT_TYPE_NAMED, 241 sizeof (filter_kstats_t) / sizeof (kstat_named_t), 242 0); 243 if (ipf_kstatp[i] != NULL) { 244 bcopy(&ipf_kstat_tmp, ipf_kstatp[i]->ks_data, 245 sizeof (filter_kstats_t)); 246 ipf_kstatp[i]->ks_update = ipf_kstat_update; 247 ipf_kstatp[i]->ks_private = &frstats[i]; 248 kstat_install(ipf_kstatp[i]); 249 } 250 } 251 252 #ifdef IPFDEBUG 253 cmn_err(CE_NOTE, "IP Filter: ipf_kstat_init() installed 0x%x, 0x%x", 254 ipf_kstatp[0], ipf_kstatp[1]); 255 #endif 256 } 257 258 static void 259 ipf_kstat_fini(void) 260 { 261 int i; 262 for (i = 0; i < 2; i++) { 263 if (ipf_kstatp[i] != NULL) { 264 kstat_delete(ipf_kstatp[i]); 265 ipf_kstatp[i] = NULL; 266 } 267 } 268 } 269 270 static int 271 ipf_kstat_update(kstat_t *ksp, int rwflag) 272 { 273 filter_kstats_t *fkp; 274 filterstats_t *fsp; 275 276 if (rwflag == KSTAT_WRITE) 277 return (EACCES); 278 279 fkp = ksp->ks_data; 280 fsp = ksp->ks_private; 281 282 fkp->fks_pass.value.ul = fsp->fr_pass; 283 fkp->fks_block.value.ul = fsp->fr_block; 284 fkp->fks_nom.value.ul = fsp->fr_nom; 285 fkp->fks_short.value.ul = fsp->fr_short; 286 fkp->fks_ppkl.value.ul = fsp->fr_ppkl; 287 fkp->fks_bpkl.value.ul = fsp->fr_bpkl; 288 fkp->fks_npkl.value.ul = fsp->fr_npkl; 289 fkp->fks_pkl.value.ul = fsp->fr_pkl; 290 fkp->fks_skip.value.ul = fsp->fr_skip; 291 fkp->fks_ret.value.ul = fsp->fr_ret; 292 fkp->fks_acct.value.ul = fsp->fr_acct; 293 fkp->fks_bnfr.value.ul = fsp->fr_bnfr; 294 fkp->fks_nfr.value.ul = fsp->fr_nfr; 295 fkp->fks_cfr.value.ul = fsp->fr_cfr; 296 fkp->fks_bads.value.ul = fsp->fr_bads; 297 fkp->fks_ads.value.ul = fsp->fr_ads; 298 fkp->fks_chit.value.ul = fsp->fr_chit; 299 fkp->fks_tcpbad.value.ul = fsp->fr_tcpbad; 300 fkp->fks_pull[0].value.ul = fsp->fr_pull[0]; 301 fkp->fks_pull[1].value.ul = fsp->fr_pull[1]; 302 fkp->fks_badsrc.value.ul = fsp->fr_badsrc; 303 fkp->fks_badttl.value.ul = fsp->fr_badttl; 304 fkp->fks_bad.value.ul = fsp->fr_bad; 305 fkp->fks_ipv6.value.ul = fsp->fr_ipv6; 306 fkp->fks_ppshit.value.ul = fsp->fr_ppshit; 307 fkp->fks_ipud.value.ul = fsp->fr_ipud; 308 309 return (0); 310 } 311 312 int _init() 313 { 314 int ipfinst; 315 316 ipf_kstat_init(); 317 318 ipfinst = mod_install(&modlink1); 319 320 if (ipfinst != 0) 321 ipf_kstat_fini(); 322 #ifdef IPFDEBUG 323 cmn_err(CE_NOTE, "IP Filter: _init() = %d", ipfinst); 324 #endif 325 return ipfinst; 326 } 327 328 329 int _fini(void) 330 { 331 int ipfinst; 332 333 ipfinst = mod_remove(&modlink1); 334 #ifdef IPFDEBUG 335 cmn_err(CE_NOTE, "IP Filter: _fini() = %d", ipfinst); 336 #endif 337 if (ipfinst == 0) 338 ipf_kstat_fini(); 339 340 return ipfinst; 341 } 342 343 344 int _info(modinfop) 345 struct modinfo *modinfop; 346 { 347 int ipfinst; 348 349 ipfinst = mod_info(&modlink1, modinfop); 350 #ifdef IPFDEBUG 351 cmn_err(CE_NOTE, "IP Filter: _info(%x) = %x", modinfop, ipfinst); 352 #endif 353 return ipfinst; 354 } 355 356 357 #if SOLARIS2 < 10 358 static int ipf_identify(dip) 359 dev_info_t *dip; 360 { 361 # ifdef IPFDEBUG 362 cmn_err(CE_NOTE, "IP Filter: ipf_identify(%x)", dip); 363 # endif 364 if (strcmp(ddi_get_name(dip), "ipf") == 0) 365 return (DDI_IDENTIFIED); 366 return (DDI_NOT_IDENTIFIED); 367 } 368 #endif 369 370 371 static int ipf_attach(dip, cmd) 372 dev_info_t *dip; 373 ddi_attach_cmd_t cmd; 374 { 375 char *s; 376 int i; 377 int instance; 378 379 #ifdef IPFDEBUG 380 cmn_err(CE_NOTE, "IP Filter: ipf_attach(%x,%x)", dip, cmd); 381 #endif 382 383 switch (cmd) 384 { 385 case DDI_ATTACH: 386 instance = ddi_get_instance(dip); 387 /* Only one instance of ipf (instance 0) can be attached. */ 388 if (instance > 0) 389 return DDI_FAILURE; 390 if (fr_running != 0) 391 return DDI_FAILURE; 392 393 #ifdef IPFDEBUG 394 cmn_err(CE_NOTE, "IP Filter: attach ipf instance %d", instance); 395 #endif 396 397 (void) ipf_property_update(dip); 398 399 for (i = 0; ((s = ipf_devfiles[i]) != NULL); i++) { 400 s = strrchr(s, '/'); 401 if (s == NULL) 402 continue; 403 s++; 404 if (ddi_create_minor_node(dip, s, S_IFCHR, i, 405 DDI_PSEUDO, 0) == 406 DDI_FAILURE) { 407 ddi_remove_minor_node(dip, NULL); 408 goto attach_failed; 409 } 410 } 411 412 ipf_dev_info = dip; 413 /* 414 * Initialize mutex's 415 */ 416 RWLOCK_INIT(&ipf_global, "ipf filter load/unload mutex"); 417 RWLOCK_INIT(&ipf_mutex, "ipf filter rwlock"); 418 RWLOCK_INIT(&ipf_frcache, "ipf cache rwlock"); 419 420 /* 421 * Lock people out while we set things up. 422 */ 423 WRITE_ENTER(&ipf_global); 424 if ((fr_running != 0) || (iplattach() == -1)) { 425 RWLOCK_EXIT(&ipf_global); 426 goto attach_failed; 427 } 428 429 fr_timer_id = timeout(fr_slowtimer, NULL, 430 drv_usectohz(500000)); 431 432 fr_running = 1; 433 434 RWLOCK_EXIT(&ipf_global); 435 436 cmn_err(CE_CONT, "!%s, running.\n", ipfilter_version); 437 438 return DDI_SUCCESS; 439 /* NOTREACHED */ 440 default: 441 break; 442 } 443 444 attach_failed: 445 #ifdef IPFDEBUG 446 cmn_err(CE_NOTE, "IP Filter: failed to attach\n"); 447 #endif 448 /* 449 * Use our own detach routine to toss 450 * away any stuff we allocated above. 451 */ 452 (void) ipf_detach(dip, DDI_DETACH); 453 return DDI_FAILURE; 454 } 455 456 457 static int ipf_detach(dip, cmd) 458 dev_info_t *dip; 459 ddi_detach_cmd_t cmd; 460 { 461 int i; 462 463 #ifdef IPFDEBUG 464 cmn_err(CE_NOTE, "IP Filter: ipf_detach(%x,%x)", dip, cmd); 465 #endif 466 switch (cmd) { 467 case DDI_DETACH: 468 if (fr_refcnt != 0) 469 return DDI_FAILURE; 470 471 if (fr_running == -2) 472 break; 473 /* 474 * Make sure we're the only one's modifying things. With 475 * this lock others should just fall out of the loop. 476 */ 477 WRITE_ENTER(&ipf_global); 478 if (fr_running <= 0) { 479 RWLOCK_EXIT(&ipf_global); 480 return DDI_FAILURE; 481 } 482 /* 483 * Make sure there is no active filter rule. 484 */ 485 if (ipfilter[0][fr_active] || ipfilter[1][fr_active] || 486 ipfilter6[0][fr_active] || ipfilter6[1][fr_active]) { 487 RWLOCK_EXIT(&ipf_global); 488 return DDI_FAILURE; 489 } 490 fr_running = -2; 491 492 RWLOCK_EXIT(&ipf_global); 493 494 if (fr_timer_id != 0) { 495 (void) untimeout(fr_timer_id); 496 fr_timer_id = 0; 497 } 498 499 /* 500 * Undo what we did in ipf_attach, freeing resources 501 * and removing things we installed. The system 502 * framework guarantees we are not active with this devinfo 503 * node in any other entry points at this time. 504 */ 505 ddi_prop_remove_all(dip); 506 i = ddi_get_instance(dip); 507 ddi_remove_minor_node(dip, NULL); 508 if (i > 0) { 509 cmn_err(CE_CONT, "IP Filter: still attached (%d)\n", i); 510 return DDI_FAILURE; 511 } 512 513 WRITE_ENTER(&ipf_global); 514 if (!ipldetach()) { 515 RWLOCK_EXIT(&ipf_global); 516 RW_DESTROY(&ipf_mutex); 517 RW_DESTROY(&ipf_frcache); 518 RW_DESTROY(&ipf_global); 519 cmn_err(CE_CONT, "!%s detached.\n", ipfilter_version); 520 return (DDI_SUCCESS); 521 } 522 RWLOCK_EXIT(&ipf_global); 523 break; 524 default: 525 break; 526 } 527 cmn_err(CE_NOTE, "IP Filter: failed to detach\n"); 528 return DDI_FAILURE; 529 } 530 531 532 /*ARGSUSED*/ 533 static int ipf_getinfo(dip, infocmd, arg, result) 534 dev_info_t *dip; 535 ddi_info_cmd_t infocmd; 536 void *arg, **result; 537 { 538 int error; 539 540 if (fr_running <= 0) 541 return DDI_FAILURE; 542 error = DDI_FAILURE; 543 #ifdef IPFDEBUG 544 cmn_err(CE_NOTE, "IP Filter: ipf_getinfo(%x,%x,%x)", dip, infocmd, arg); 545 #endif 546 switch (infocmd) { 547 case DDI_INFO_DEVT2DEVINFO: 548 *result = ipf_dev_info; 549 error = DDI_SUCCESS; 550 break; 551 case DDI_INFO_DEVT2INSTANCE: 552 *result = (void *)0; 553 error = DDI_SUCCESS; 554 break; 555 default: 556 break; 557 } 558 return (error); 559 } 560 561 562 /* 563 * Fetch configuration file values that have been entered into the ipf.conf 564 * driver file. 565 */ 566 static int ipf_property_update(dip) 567 dev_info_t *dip; 568 { 569 ipftuneable_t *ipft; 570 int64_t *i64p; 571 char *name; 572 u_int one; 573 int *i32p; 574 int err; 575 576 #ifdef DDI_NO_AUTODETACH 577 if (ddi_prop_update_int(DDI_DEV_T_NONE, dip, 578 DDI_NO_AUTODETACH, 1) != DDI_PROP_SUCCESS) { 579 cmn_err(CE_WARN, "!updating DDI_NO_AUTODETACH failed"); 580 return DDI_FAILURE; 581 } 582 #else 583 if (ddi_prop_update_int(DDI_DEV_T_NONE, dip, 584 "ddi-no-autodetach", 1) != DDI_PROP_SUCCESS) { 585 cmn_err(CE_WARN, "!updating ddi-no-autodetach failed"); 586 return DDI_FAILURE; 587 } 588 #endif 589 590 err = DDI_SUCCESS; 591 ipft = ipf_tuneables; 592 for (ipft = ipf_tuneables; (name = ipft->ipft_name) != NULL; ipft++) { 593 one = 1; 594 switch (ipft->ipft_sz) 595 { 596 case 4 : 597 i32p = NULL; 598 err = ddi_prop_lookup_int_array(DDI_DEV_T_ANY, dip, 599 0, name, &i32p, &one); 600 if (err == DDI_PROP_NOT_FOUND) 601 continue; 602 #ifdef IPFDEBUG 603 cmn_err(CE_CONT, "IP Filter: lookup_int(%s) = %d\n", 604 name, err); 605 #endif 606 if (err != DDI_PROP_SUCCESS) 607 return err; 608 if (*i32p >= ipft->ipft_min && *i32p <= ipft->ipft_max) 609 *ipft->ipft_pint = *i32p; 610 else 611 err = DDI_PROP_CANNOT_DECODE; 612 ddi_prop_free(i32p); 613 break; 614 615 #if SOLARIS2 > 8 616 case 8 : 617 i64p = NULL; 618 err = ddi_prop_lookup_int64_array(DDI_DEV_T_ANY, dip, 619 0, name, &i64p, &one); 620 if (err == DDI_PROP_NOT_FOUND) 621 continue; 622 # ifdef IPFDEBUG 623 cmn_err(CE_CONT, "IP Filter: lookup_int64(%s) = %d\n", 624 name, err); 625 # endif 626 if (err != DDI_PROP_SUCCESS) 627 return err; 628 if (*i64p >= ipft->ipft_min && *i64p <= ipft->ipft_max) 629 *ipft->ipft_pint = *i64p; 630 else 631 err = DDI_PROP_CANNOT_DECODE; 632 ddi_prop_free(i64p); 633 break; 634 #endif 635 636 default : 637 break; 638 } 639 if (err != DDI_SUCCESS) 640 break; 641 } 642 643 return err; 644 } 645